#include "pch.h" #include "ModelSearchHandler.h" #include #include // Main HTTP request handler HttpResponse ModelSearchHandler::HandleModelSearchRequest(const HttpRequest& request) { HttpResponse response; // Validate request method if (request.method != "POST") { return FormatErrorResponse(405, "Method not allowed. Use POST."); } // Validate request std::string error_message; if (!ValidateHttpRequest(request, error_message)) { return FormatErrorResponse(400, error_message); } try { // Parse search request ModelSearchRequest search_request = ParseSearchRequest(request.body); // Execute search ModelSearchResult search_result = ModelSearchEngine::Instance().SearchModels(search_request); // Format response if (search_result.success) { response = FormatSuccessResponse(search_result); } else { response = FormatErrorResponse(500, search_result.error_message); } } catch (const std::exception& e) { response = FormatErrorResponse(500, "Request processing error: " + std::string(e.what())); } catch (...) { response = FormatErrorResponse(500, "Unknown error during model search"); } return response; } // Request validation method bool ModelSearchHandler::ValidateHttpRequest(const HttpRequest& request, std::string& error_message) { if (request.body.empty()) { error_message = "Request body cannot be empty"; return false; } // Check basic JSON format if (request.body.find("{") == std::string::npos || request.body.find("}") == std::string::npos) { error_message = "Invalid JSON format in request body"; return false; } return true; } // Parse search request ModelSearchRequest ModelSearchHandler::ParseSearchRequest(const std::string& json_body) { ModelSearchRequest request; // Parse required parameters request.query = Trim(ExtractJsonValue(json_body, "query")); if (request.query.empty()) { request.query = Trim(ExtractJsonValue(json_body, "search_query")); } // Parse optional parameters std::string match_mode = ExtractJsonValue(json_body, "match_mode"); if (!match_mode.empty()) { request.match_mode = match_mode; } std::string search_scope = ExtractJsonValue(json_body, "search_scope"); if (!search_scope.empty()) { request.search_scope = search_scope; } // Parse model type filter std::vector model_types = ExtractJsonArrayValue(json_body, "model_types"); if (!model_types.empty()) { request.model_types = model_types; } // Parse numeric parameters int max_results = ExtractJsonIntValue(json_body, "max_results", 0); if (max_results > 0) { request.max_results = max_results; } double similarity_threshold = ExtractJsonDoubleValue(json_body, "similarity_threshold", -1.0); if (similarity_threshold >= 0.0 && similarity_threshold <= 1.0) { request.similarity_threshold = similarity_threshold; } // Parse boolean parameters request.include_components = ExtractJsonBoolValue(json_body, "include_components", request.include_components); request.include_features = ExtractJsonBoolValue(json_body, "include_features", request.include_features); return request; } // Format success response HttpResponse ModelSearchHandler::FormatSuccessResponse(const ModelSearchResult& result) { HttpResponse response; response.status_code = 200; std::ostringstream json; json << "{" << "\"success\": true," << "\"data\": {" << "\"results\": ["; // Build search results list bool first_item = true; for (const auto& item : result.results) { if (!first_item) json << ","; first_item = false; json << "{" << "\"model_name\": \"" << EscapeJsonString(item.model_name) << "\"," << "\"full_path\": \"" << EscapeJsonString(item.full_path) << "\"," << "\"file_path\": \"" << EscapeJsonString(item.file_path) << "\"," << "\"model_type\": \"" << EscapeJsonString(item.model_type) << "\"," << "\"match_score\": " << item.match_score << "," << "\"match_reason\": \"" << EscapeJsonString(item.match_reason) << "\"," << "\"match_type\": \"" << EscapeJsonString(item.match_type) << "\"," << "\"is_in_session\": " << (item.is_in_session ? "true" : "false") << "," << "\"is_assembly\": " << (item.is_assembly ? "true" : "false") << "," << "\"component_count\": " << item.component_count << "," << "\"matched_keywords\": ["; // Build matched keywords list bool first_keyword = true; for (const auto& keyword : item.matched_keywords) { if (!first_keyword) json << ","; first_keyword = false; json << "\"" << EscapeJsonString(keyword) << "\""; } json << "]" << "}"; } json << "]," << "\"total_found\": " << result.total_found << "," << "\"returned_count\": " << result.returned_count << "," << "\"search_time_ms\": \"" << EscapeJsonString(result.search_time_ms) << "\"," << "\"stats\": {" << "\"session_models_count\": " << result.stats.session_models_count << "," << "\"components_searched\": " << result.stats.components_searched << "," << "\"features_searched\": " << result.stats.features_searched << "," << "\"search_scope_used\": \"" << EscapeJsonString(result.stats.search_scope_used) << "\"" << "}" << "}," << "\"error\": null" << "}"; response.body = json.str(); return response; } // Format error response HttpResponse ModelSearchHandler::FormatErrorResponse(int status_code, const std::string& error_message) { HttpResponse response; response.status_code = status_code; std::ostringstream json; json << "{" << "\"success\": false," << "\"data\": null," << "\"error\": \"" << EscapeJsonString(error_message) << "\"" << "}"; response.body = json.str(); return response; } // JSON parsing helper methods std::string ModelSearchHandler::ExtractJsonValue(const std::string& json, const std::string& key) { // Find key-value pair "key": "value" or "key":"value" std::string key_pattern = "\"" + key + "\""; size_t key_pos = json.find(key_pattern); if (key_pos != std::string::npos) { // Find colon size_t colon_pos = json.find(":", key_pos); if (colon_pos != std::string::npos) { // Skip spaces to find value start size_t value_start = colon_pos + 1; while (value_start < json.length() && (json[value_start] == ' ' || json[value_start] == '\t' || json[value_start] == '\n' || json[value_start] == '\r')) { value_start++; } // Check if it's a string value (starts with double quote) if (value_start < json.length() && json[value_start] == '"') { value_start++; // Skip opening quote size_t value_end = json.find('"', value_start); if (value_end != std::string::npos) { return json.substr(value_start, value_end - value_start); } } else { // Non-string value, find until comma, brace or string end size_t value_end = value_start; while (value_end < json.length() && json[value_end] != ',' && json[value_end] != '}' && json[value_end] != ']' && json[value_end] != '\n') { value_end++; } std::string value = Trim(json.substr(value_start, value_end - value_start)); return value; } } } return ""; } bool ModelSearchHandler::ExtractJsonBoolValue(const std::string& json, const std::string& key, bool default_value) { std::string value = ExtractJsonValue(json, key); if (value.empty()) { return default_value; } // Convert to lowercase for comparison std::transform(value.begin(), value.end(), value.begin(), ::tolower); return (value == "true" || value == "1"); } int ModelSearchHandler::ExtractJsonIntValue(const std::string& json, const std::string& key, int default_value) { std::string value = ExtractJsonValue(json, key); if (value.empty()) { return default_value; } try { return std::stoi(value); } catch (...) { return default_value; } } double ModelSearchHandler::ExtractJsonDoubleValue(const std::string& json, const std::string& key, double default_value) { std::string value = ExtractJsonValue(json, key); if (value.empty()) { return default_value; } try { return std::stod(value); } catch (...) { return default_value; } } std::vector ModelSearchHandler::ExtractJsonArrayValue(const std::string& json, const std::string& key) { std::vector result; // Find key-value pair "key": [...] std::string key_pattern = "\"" + key + "\""; size_t key_pos = json.find(key_pattern); if (key_pos != std::string::npos) { // Find colon size_t colon_pos = json.find(":", key_pos); if (colon_pos != std::string::npos) { // Skip spaces to find array start size_t array_start = colon_pos + 1; while (array_start < json.length() && (json[array_start] == ' ' || json[array_start] == '\t' || json[array_start] == '\n' || json[array_start] == '\r')) { array_start++; } // Check if it's an array (starts with [) if (array_start < json.length() && json[array_start] == '[') { size_t array_end = json.find(']', array_start); if (array_end != std::string::npos) { std::string array_content = json.substr(array_start + 1, array_end - array_start - 1); // Parse array elements size_t pos = 0; while (pos < array_content.length()) { // Skip spaces while (pos < array_content.length() && (array_content[pos] == ' ' || array_content[pos] == '\t' || array_content[pos] == '\n' || array_content[pos] == '\r')) { pos++; } if (pos >= array_content.length()) break; // Find string value if (array_content[pos] == '"') { pos++; // Skip opening quote size_t end_quote = array_content.find('"', pos); if (end_quote != std::string::npos) { result.push_back(array_content.substr(pos, end_quote - pos)); pos = end_quote + 1; } } // Find next comma size_t next_comma = array_content.find(',', pos); if (next_comma != std::string::npos) { pos = next_comma + 1; } else { break; } } } } } } return result; } // JSON escape method std::string ModelSearchHandler::EscapeJsonString(const std::string& input) { std::string escaped; escaped.reserve(input.length() * 2); for (char c : input) { switch (c) { case '"': escaped += "\\\""; break; case '\\': escaped += "\\\\"; break; case '\b': escaped += "\\b"; break; case '\f': escaped += "\\f"; break; case '\n': escaped += "\\n"; break; case '\r': escaped += "\\r"; break; case '\t': escaped += "\\t"; break; default: if (c < 0x20) { // Control character escape to Unicode char buffer[7]; snprintf(buffer, sizeof(buffer), "\\u%04x", static_cast(c)); escaped += buffer; } else { escaped += c; } break; } } return escaped; } // Helper utility methods std::string ModelSearchHandler::Trim(const std::string& str) { size_t start = str.find_first_not_of(" \t\n\r"); if (start == std::string::npos) return ""; size_t end = str.find_last_not_of(" \t\n\r"); return str.substr(start, end - start + 1); } bool ModelSearchHandler::StartsWith(const std::string& str, const std::string& prefix) { return str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix; } bool ModelSearchHandler::EndsWith(const std::string& str, const std::string& suffix) { return str.size() >= suffix.size() && str.substr(str.size() - suffix.size()) == suffix; }