Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| Test script for the Multi-Model Chat Backend API with strict typing | |
| """ | |
| import requests | |
| import time | |
| from typing import Dict, Any, List, Optional, Callable | |
| from dataclasses import dataclass | |
| from enum import Enum | |
| from custom_types import ( | |
| create_model_id, create_conversation_id, create_user_message, | |
| create_assistant_response | |
| ) | |
| # Configuration | |
| API_BASE_URL = "http://localhost:7860" | |
| TEST_MODELS = [ | |
| "thoughtcast/outlandish-spiked-lassie-experiment", | |
| "thoughtcast/marketing-spiked-lassie-experiment", | |
| "TinyLlama/TinyLlama-1.1B-Chat-v1.0" | |
| ] | |
| class TestStatus(Enum): | |
| """Test status enumeration.""" | |
| PASSED = "passed" | |
| FAILED = "failed" | |
| ERROR = "error" | |
| class TestResult: | |
| """Test result with strict typing.""" | |
| name: str | |
| status: TestStatus | |
| message: str | |
| details: Optional[Dict[str, Any]] = None | |
| class ChatTestRequest: | |
| """Chat test request with validation.""" | |
| model_id: str | |
| message: str | |
| conversation_id: str = "test" | |
| max_new_tokens: int = 50 | |
| temperature: float = 0.7 | |
| top_p: float = 0.9 | |
| repetition_penalty: float = 1.1 | |
| no_repeat_ngram_size: int = 3 | |
| do_sample: bool = True | |
| def to_payload(self) -> Dict[str, Any]: | |
| """Convert to API payload with validation.""" | |
| # Validate inputs using our type system | |
| model_id_wrapper = create_model_id(self.model_id) | |
| conv_id_wrapper = create_conversation_id(self.conversation_id) | |
| message_wrapper = create_user_message(self.message) | |
| return { | |
| "message": message_wrapper.unwrap(), | |
| "conversation_id": conv_id_wrapper.unwrap(), | |
| "model_id": model_id_wrapper.unwrap(), | |
| "max_new_tokens": self.max_new_tokens, | |
| "temperature": self.temperature, | |
| "top_p": self.top_p, | |
| "repetition_penalty": self.repetition_penalty, | |
| "no_repeat_ngram_size": self.no_repeat_ngram_size, | |
| "do_sample": self.do_sample | |
| } | |
| def test_health_check() -> TestResult: | |
| """Test the health check endpoint.""" | |
| print("Testing health check...") | |
| try: | |
| response = requests.get(f"{API_BASE_URL}/health") | |
| if response.status_code == 200: | |
| data = response.json() | |
| print(f"β Health check passed: {data['status']}") | |
| print(f" Models loaded: {data['models_loaded']}") | |
| print(f" GPU memory used: {data['gpu_memory_used']:.2%}") | |
| print(f" Active conversations: {data['active_conversations']}") | |
| return TestResult( | |
| name="health_check", | |
| status=TestStatus.PASSED, | |
| message="Health check successful", | |
| details=data | |
| ) | |
| else: | |
| print(f"β Health check failed: {response.status_code}") | |
| return TestResult( | |
| name="health_check", | |
| status=TestStatus.FAILED, | |
| message=f"HTTP {response.status_code}: {response.text}" | |
| ) | |
| except Exception as e: | |
| print(f"β Health check error: {e}") | |
| return TestResult( | |
| name="health_check", | |
| status=TestStatus.ERROR, | |
| message=str(e) | |
| ) | |
| def test_list_models() -> TestResult: | |
| """Test the list models endpoint.""" | |
| print("\nTesting list models...") | |
| try: | |
| response = requests.get(f"{API_BASE_URL}/models") | |
| if response.status_code == 200: | |
| data = response.json() | |
| print(f"β Models endpoint working") | |
| print(f" Available models: {data['available_models']}") | |
| print(f" Loaded models: {data['loaded_models']}") | |
| # Validate model IDs | |
| for model_id in data['available_models']: | |
| try: | |
| _ = create_model_id(model_id) | |
| except ValueError as e: | |
| return TestResult( | |
| name="list_models", | |
| status=TestStatus.FAILED, | |
| message=f"Invalid model ID format: {model_id} - {e}" | |
| ) | |
| return TestResult( | |
| name="list_models", | |
| status=TestStatus.PASSED, | |
| message="Models endpoint working", | |
| details=data | |
| ) | |
| else: | |
| print(f"β Models endpoint failed: {response.status_code}") | |
| return TestResult( | |
| name="list_models", | |
| status=TestStatus.FAILED, | |
| message=f"HTTP {response.status_code}: {response.text}" | |
| ) | |
| except Exception as e: | |
| print(f"β Models endpoint error: {e}") | |
| return TestResult( | |
| name="list_models", | |
| status=TestStatus.ERROR, | |
| message=str(e) | |
| ) | |
| def test_chat(model_id: str, message: str, conversation_id: str = "test") -> TestResult: | |
| """Test the chat endpoint with a specific model.""" | |
| print(f"\nTesting chat with {model_id}...") | |
| try: | |
| # Create and validate chat request | |
| chat_request = ChatTestRequest( | |
| model_id=model_id, | |
| message=message, | |
| conversation_id=conversation_id | |
| ) | |
| payload = chat_request.to_payload() | |
| response = requests.post(f"{API_BASE_URL}/chat", json=payload) | |
| if response.status_code == 200: | |
| data = response.json() | |
| # Validate response | |
| try: | |
| _ = create_model_id(data['model_id']) | |
| _ = create_conversation_id(data['conversation_id']) | |
| _ = create_assistant_response(data['response']) | |
| except ValueError as e: | |
| return TestResult( | |
| name=f"chat_{model_id.replace('/', '_')}", | |
| status=TestStatus.FAILED, | |
| message=f"Invalid response format: {e}" | |
| ) | |
| print(f"β Chat successful with {model_id}") | |
| print(f" Response: {data['response'][:100]}...") | |
| print(f" Tokens used: {data['tokens_used']}") | |
| return TestResult( | |
| name=f"chat_{model_id.replace('/', '_')}", | |
| status=TestStatus.PASSED, | |
| message=f"Chat successful with {model_id}", | |
| details=data | |
| ) | |
| else: | |
| print(f"β Chat failed with {model_id}: {response.status_code}") | |
| print(f" Error: {response.text}") | |
| return TestResult( | |
| name=f"chat_{model_id.replace('/', '_')}", | |
| status=TestStatus.FAILED, | |
| message=f"HTTP {response.status_code}: {response.text}" | |
| ) | |
| except ValueError as e: | |
| print(f"β Chat validation error with {model_id}: {e}") | |
| return TestResult( | |
| name=f"chat_{model_id.replace('/', '_')}", | |
| status=TestStatus.ERROR, | |
| message=f"Validation error: {e}" | |
| ) | |
| except Exception as e: | |
| print(f"β Chat error with {model_id}: {e}") | |
| return TestResult( | |
| name=f"chat_{model_id.replace('/', '_')}", | |
| status=TestStatus.ERROR, | |
| message=str(e) | |
| ) | |
| def test_conversation_management() -> TestResult: | |
| """Test conversation management endpoints.""" | |
| print("\nTesting conversation management...") | |
| try: | |
| # List conversations | |
| response = requests.get(f"{API_BASE_URL}/conversations") | |
| if response.status_code == 200: | |
| data = response.json() | |
| print(f"β List conversations working") | |
| print(f" Active conversations: {len(data['conversations'])}") | |
| # Get specific conversation | |
| test_conv_id = "test" | |
| try: | |
| _ = create_conversation_id(test_conv_id) | |
| except ValueError as e: | |
| return TestResult( | |
| name="conversation_management", | |
| status=TestStatus.ERROR, | |
| message=f"Invalid test conversation ID: {e}" | |
| ) | |
| response = requests.get(f"{API_BASE_URL}/conversations/{test_conv_id}") | |
| if response.status_code == 200: | |
| data = response.json() | |
| print(f"β Get conversation working") | |
| print(f" Messages in conversation: {data['message_count']}") | |
| # Export conversation | |
| response = requests.post(f"{API_BASE_URL}/conversations/{test_conv_id}/export") | |
| if response.status_code == 200: | |
| data = response.json() | |
| print(f"β Export conversation working") | |
| print(f" Exported {len(data['messages'])} messages") | |
| return TestResult( | |
| name="conversation_management", | |
| status=TestStatus.PASSED, | |
| message="Conversation management working" | |
| ) | |
| except Exception as e: | |
| print(f"β Conversation management error: {e}") | |
| return TestResult( | |
| name="conversation_management", | |
| status=TestStatus.ERROR, | |
| message=str(e) | |
| ) | |
| def test_model_loading() -> TestResult: | |
| """Test model loading and unloading.""" | |
| print("\nTesting model loading/unloading...") | |
| try: | |
| # Try to load a model | |
| model_id = "TinyLlama/TinyLlama-1.1B-Chat-v1.0" | |
| # Validate model ID | |
| try: | |
| _ = create_model_id(model_id) | |
| except ValueError as e: | |
| return TestResult( | |
| name="model_loading", | |
| status=TestStatus.ERROR, | |
| message=f"Invalid model ID: {e}" | |
| ) | |
| response = requests.post(f"{API_BASE_URL}/models/{model_id}/load") | |
| if response.status_code == 200: | |
| print(f"β Model loading working for {model_id}") | |
| return TestResult( | |
| name="model_loading", | |
| status=TestStatus.PASSED, | |
| message=f"Model loading successful for {model_id}" | |
| ) | |
| else: | |
| print(f"β Model loading failed for {model_id}: {response.status_code}") | |
| return TestResult( | |
| name="model_loading", | |
| status=TestStatus.FAILED, | |
| message=f"HTTP {response.status_code}: {response.text}" | |
| ) | |
| except Exception as e: | |
| print(f"β Model loading error: {e}") | |
| return TestResult( | |
| name="model_loading", | |
| status=TestStatus.ERROR, | |
| message=str(e) | |
| ) | |
| def run_comprehensive_test() -> bool: | |
| """Run all tests with strict typing.""" | |
| print("π Starting comprehensive API tests...") | |
| print("=" * 60) | |
| # Wait for service to be ready | |
| print("Waiting for service to be ready...") | |
| for _ in range(30): # Wait up to 30 seconds | |
| health_result = test_health_check() | |
| if health_result.status == TestStatus.PASSED: | |
| break | |
| time.sleep(1) | |
| else: | |
| print("β Service not ready after 30 seconds") | |
| return False | |
| # Define test functions | |
| test_functions: List[Callable[[], TestResult]] = [ | |
| test_list_models, | |
| lambda: test_chat("thoughtcast/outlandish-spiked-lassie-experiment", "Hello, how are you?"), | |
| lambda: test_chat("thoughtcast/marketing-spiked-lassie-experiment", "Tell me about your marketing strategy"), | |
| lambda: test_chat("TinyLlama/TinyLlama-1.1B-Chat-v1.0", "What is machine learning?"), | |
| test_conversation_management, | |
| test_model_loading, | |
| ] | |
| # Run tests and collect results | |
| results: List[TestResult] = [] | |
| for test_func in test_functions: | |
| result = test_func() | |
| results.append(result) | |
| # Analyze results | |
| passed = sum(1 for r in results if r.status == TestStatus.PASSED) | |
| failed = sum(1 for r in results if r.status == TestStatus.FAILED) | |
| errors = sum(1 for r in results if r.status == TestStatus.ERROR) | |
| print("\n" + "=" * 60) | |
| print(f"π― Test Results: {passed}/{len(results)} tests passed") | |
| print(f" Passed: {passed}") | |
| print(f" Failed: {failed}") | |
| print(f" Errors: {errors}") | |
| # Print failed/error tests | |
| if failed > 0 or errors > 0: | |
| print("\nβ Failed/Error Tests:") | |
| for result in results: | |
| if result.status != TestStatus.PASSED: | |
| print(f" {result.name}: {result.status.value} - {result.message}") | |
| if passed == len(results): | |
| print("π All tests passed! The API is working correctly.") | |
| return True | |
| else: | |
| print("β Some tests failed. Please check the logs.") | |
| return False | |
| if __name__ == "__main__": | |
| success = run_comprehensive_test() | |
| exit(0 if success else 1) |