Spaces:
Sleeping
Sleeping
| from flask import Blueprint, jsonify, request | |
| from http import HTTPStatus | |
| from typing import Dict, Any | |
| from lpm_kernel.api.dto.user_llm_config_dto import UpdateUserLLMConfigDTO | |
| from lpm_kernel.api.services.user_llm_config_service import UserLLMConfigService | |
| from lpm_kernel.api.common.responses import APIResponse | |
| from lpm_kernel.common.logging import logger | |
| user_llm_config_bp = Blueprint("user_llm_config", __name__, url_prefix="/api/user-llm-configs") | |
| user_llm_config_service = UserLLMConfigService() | |
| # OpenAI configuration constants | |
| OPENAI_ENDPOINT = "https://api.openai.com/v1" | |
| def validate_llm_config(data: Dict[Any, Any]) -> Dict[str, str]: | |
| """Validate LLM configuration based on provider type | |
| Args: | |
| data: Configuration data | |
| Returns: | |
| Dictionary with error messages if validation fails, empty dict if validation passes | |
| """ | |
| errors = {} | |
| provider_type = data.get('provider_type') | |
| if provider_type == 'openai': | |
| # For OpenAI, key is required | |
| if not data.get('key'): | |
| errors['key'] = 'API key is required for OpenAI provider' | |
| elif provider_type: | |
| # For custom providers, all endpoint and key fields are required | |
| required_fields = [ | |
| 'chat_endpoint', 'chat_api_key', 'chat_model_name', | |
| 'embedding_endpoint', 'embedding_api_key', 'embedding_model_name' | |
| ] | |
| for field in required_fields: | |
| if not data.get(field): | |
| errors[field] = f'{field} is required for custom provider' | |
| else: | |
| errors['provider_type'] = 'provider_type is required' | |
| return errors | |
| def validate_thinking_model(data: Dict[Any, Any]) -> Dict[str, str]: | |
| """Validate thinking model configuration | |
| Args: | |
| data: Configuration data | |
| Returns: | |
| Dictionary with error messages if validation fails, empty dict if validation passes | |
| """ | |
| errors = {} | |
| # Validate required fields | |
| if not data.get('thinking_model_name'): | |
| errors['thinking_model_name'] = 'Thinking model name is required' | |
| if not data.get('thinking_endpoint'): | |
| errors['thinking_endpoint'] = 'Thinking endpoint is required' | |
| return errors | |
| def process_openai_config(data: Dict[Any, Any]) -> Dict[Any, Any]: | |
| """Process OpenAI configuration, using simplified config if provider type is OpenAI""" | |
| if data.get('provider_type') == 'openai' and data.get('key'): | |
| # Use the unified key to fill chat and embedding api_keys | |
| data['chat_api_key'] = data['key'] | |
| data['chat_model_name'] ='gpt-4o-mini' | |
| data['embedding_api_key'] = data['key'] | |
| data['embedding_model_name'] = 'text-embedding-ada-002' | |
| data['chat_endpoint'] = OPENAI_ENDPOINT | |
| data['embedding_endpoint'] = OPENAI_ENDPOINT | |
| return data | |
| def get_config(): | |
| """Get LLM configuration""" | |
| try: | |
| config = user_llm_config_service.get_available_llm() # Default configuration ID is 1 | |
| if not config: | |
| # Return null instead of 404 when no configuration is found | |
| return jsonify( | |
| APIResponse.success( | |
| data=None, | |
| message="No LLM configuration found" | |
| ) | |
| ), HTTPStatus.OK | |
| return jsonify( | |
| APIResponse.success( | |
| data=config.dict(), | |
| message="Successfully retrieved LLM configuration" | |
| ) | |
| ), HTTPStatus.OK | |
| except Exception as e: | |
| logger.error(f"Failed to retrieve configuration: {str(e)}", exc_info=True) | |
| return jsonify( | |
| APIResponse.error(f"Failed to retrieve configuration: {str(e)}") | |
| ), HTTPStatus.INTERNAL_SERVER_ERROR | |
| def update_config(): | |
| """Update LLM configuration""" | |
| try: | |
| # Validate request data | |
| request_data = request.json | |
| validation_errors = validate_llm_config(request_data) | |
| if validation_errors: | |
| error_message = "; ".join([f"{k}: {v}" for k, v in validation_errors.items()]) | |
| return jsonify( | |
| APIResponse.error(f"Validation failed: {error_message}") | |
| ), HTTPStatus.BAD_REQUEST | |
| # Process request data | |
| processed_data = process_openai_config(request_data) | |
| data = UpdateUserLLMConfigDTO(**processed_data) | |
| config = user_llm_config_service.update_config(1, data) # Default configuration ID is 1 | |
| return jsonify( | |
| APIResponse.success( | |
| data=config.dict(), | |
| message="Configuration updated successfully" | |
| ) | |
| ), HTTPStatus.OK | |
| except Exception as e: | |
| logger.error(f"Failed to update configuration: {str(e)}", exc_info=True) | |
| return jsonify( | |
| APIResponse.error(f"Failed to update configuration: {str(e)}") | |
| ), HTTPStatus.INTERNAL_SERVER_ERROR | |
| def update_thinking_model(): | |
| """Update thinking model configuration""" | |
| try: | |
| # Validate request data | |
| request_data = request.json | |
| validation_errors = validate_thinking_model(request_data) | |
| if validation_errors: | |
| error_message = "; ".join([f"{k}: {v}" for k, v in validation_errors.items()]) | |
| return jsonify( | |
| APIResponse.error(f"Validation failed: {error_message}") | |
| ), HTTPStatus.BAD_REQUEST | |
| # Create a DTO with only thinking model fields | |
| thinking_data = {} | |
| if 'thinking_model_name' in request_data: | |
| thinking_data['thinking_model_name'] = request_data['thinking_model_name'] | |
| if 'thinking_endpoint' in request_data: | |
| thinking_data['thinking_endpoint'] = request_data['thinking_endpoint'] | |
| if 'thinking_api_key' in request_data: | |
| thinking_data['thinking_api_key'] = request_data['thinking_api_key'] | |
| # Update the configuration | |
| data = UpdateUserLLMConfigDTO(**thinking_data) | |
| config = user_llm_config_service.update_config(1, data) # Default configuration ID is 1 | |
| return jsonify( | |
| APIResponse.success( | |
| data=config.dict(), | |
| message="Thinking model configuration updated successfully" | |
| ) | |
| ), HTTPStatus.OK | |
| except Exception as e: | |
| logger.error(f"Failed to update thinking model configuration: {str(e)}", exc_info=True) | |
| return jsonify( | |
| APIResponse.error(f"Failed to update thinking model configuration: {str(e)}") | |
| ), HTTPStatus.INTERNAL_SERVER_ERROR | |
| def delete_key(): | |
| """Delete API key from LLM configuration""" | |
| try: | |
| # use default configuration ID (1) | |
| config = user_llm_config_service.delete_key() | |
| if not config: | |
| return jsonify( | |
| APIResponse.error("No LLM configuration found") | |
| ), HTTPStatus.NOT_FOUND | |
| return jsonify( | |
| APIResponse.success( | |
| data=config.dict(), | |
| message="API key deleted successfully" | |
| ) | |
| ), HTTPStatus.OK | |
| except Exception as e: | |
| logger.error(f"Failed to delete API key: {str(e)}", exc_info=True) | |
| return jsonify( | |
| APIResponse.error(f"Failed to delete API key: {str(e)}") | |
| ), HTTPStatus.INTERNAL_SERVER_ERROR | |