Create step OpenSearch

This commit is contained in:
2026-01-15 09:51:40 +01:00
parent b86282ce97
commit 25246e3405
2 changed files with 321 additions and 0 deletions

View File

@@ -72,6 +72,7 @@ import com.azure.core.credential.AzureKeyCredential;
import com.olympus.hermione.stepSolvers.ExternalCodeGenieSolver; import com.olympus.hermione.stepSolvers.ExternalCodeGenieSolver;
import com.olympus.hermione.stepSolvers.OlympusAgentSolver; import com.olympus.hermione.stepSolvers.OlympusAgentSolver;
import com.olympus.hermione.stepSolvers.OlynmpusChatClientSolver; import com.olympus.hermione.stepSolvers.OlynmpusChatClientSolver;
import com.olympus.hermione.stepSolvers.OpenSearchQuerySolver;
@Service @Service
public class ScenarioExecutionService { public class ScenarioExecutionService {
@@ -282,6 +283,9 @@ public class ScenarioExecutionService {
case "DELETE_TEMPORARY_DOC": case "DELETE_TEMPORARY_DOC":
solver = new DeleteDocTempSolver(); solver = new DeleteDocTempSolver();
break; break;
case "OPENSEARCH_RAG_QUERY":
solver = new OpenSearchQuerySolver();
break;
default: default:
break; break;
} }

View File

@@ -0,0 +1,317 @@
package com.olympus.hermione.stepSolvers;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import com.olympus.hermione.models.ScenarioExecution;
import com.olympus.hermione.utility.AttributeParser;
import ch.qos.logback.classic.Logger;
/**
* OpenSearchQuerySolver integrates the OpenSearch document search service
* into Hermione workflows. Replaces the legacy vector search with the new
* unified search API that supports multiple search types (semantic, keyword,
* fulltext, hybrid) and advanced filtering.
*/
public class OpenSearchQuerySolver extends StepSolver {
private String opensearch_base_url;
// private String opensearch_project;
private String opensearch_query;
private String opensearch_search_type;
private Integer opensearch_k;
private Integer opensearch_max_context_chars;
private Double opensearch_vector_weight;
private Double opensearch_entity_weight;
private String opensearch_output_variable;
// Optional Ks* filter fields
// private String opensearch_ks_application_name;
// private String opensearch_ks_doctype;
// private String opensearch_ks_doc_source;
// private String opensearch_ks_file_source;
// private String opensearch_ks_document_id;
// private String opensearch_ks_project_name;
// private String opensearch_ks_knowledge_path;
// Optional tags filter (as JSON string)
// private String opensearch_tags;
Logger logger = (Logger) LoggerFactory.getLogger(OpenSearchQuerySolver.class);
private void loadParameters(){
logger.info("Loading parameters for OpenSearch Query");
// Base URL (required)
if(this.step.getAttributes().get("opensearch_base_url") != null){
this.opensearch_base_url = (String) this.step.getAttributes().get("opensearch_base_url");
logger.info("opensearch_base_url: " + this.opensearch_base_url);
} else {
throw new IllegalArgumentException("opensearch_base_url is required");
}
// Project (required)
// if(this.step.getAttributes().get("opensearch_project") != null){
// this.opensearch_project = (String) this.step.getAttributes().get("opensearch_project");
// logger.info("opensearch_project: " + this.opensearch_project);
// } else {
// throw new IllegalArgumentException("opensearch_project is required");
// }
// Query (required)
if(this.step.getAttributes().get("opensearch_query") != null){
this.opensearch_query = (String) this.step.getAttributes().get("opensearch_query");
logger.info("opensearch_query: " + this.opensearch_query);
} else {
throw new IllegalArgumentException("opensearch_query is required");
}
// Search type (default: hybrid)
if(this.step.getAttributes().get("opensearch_search_type") != null){
this.opensearch_search_type = (String) this.step.getAttributes().get("opensearch_search_type");
} else {
this.opensearch_search_type = "hybrid";
}
logger.info("opensearch_search_type: " + this.opensearch_search_type);
// K (number of results, default: 5)
if(this.step.getAttributes().get("opensearch_k") != null){
Object kValue = this.step.getAttributes().get("opensearch_k");
if(kValue instanceof Integer){
this.opensearch_k = (Integer) kValue;
} else {
this.opensearch_k = Integer.parseInt(kValue.toString());
}
} else {
this.opensearch_k = 5;
}
logger.info("opensearch_k: " + this.opensearch_k);
// Max context chars (default: 4000)
if(this.step.getAttributes().get("opensearch_max_context_chars") != null){
Object maxCharsValue = this.step.getAttributes().get("opensearch_max_context_chars");
if(maxCharsValue instanceof Integer){
this.opensearch_max_context_chars = (Integer) maxCharsValue;
} else {
this.opensearch_max_context_chars = Integer.parseInt(maxCharsValue.toString());
}
} else {
this.opensearch_max_context_chars = 4000;
}
logger.info("opensearch_max_context_chars: " + this.opensearch_max_context_chars);
// Vector weight (default: 0.7)
if(this.step.getAttributes().get("opensearch_vector_weight") != null){
Object weightValue = this.step.getAttributes().get("opensearch_vector_weight");
if(weightValue instanceof Double){
this.opensearch_vector_weight = (Double) weightValue;
} else {
this.opensearch_vector_weight = Double.parseDouble(weightValue.toString());
}
} else {
this.opensearch_vector_weight = 0.7;
}
logger.info("opensearch_vector_weight: " + this.opensearch_vector_weight);
// Entity weight (default: 0.3)
if(this.step.getAttributes().get("opensearch_entity_weight") != null){
Object weightValue = this.step.getAttributes().get("opensearch_entity_weight");
if(weightValue instanceof Double){
this.opensearch_entity_weight = (Double) weightValue;
} else {
this.opensearch_entity_weight = Double.parseDouble(weightValue.toString());
}
} else {
this.opensearch_entity_weight = 0.3;
}
logger.info("opensearch_entity_weight: " + this.opensearch_entity_weight);
// Output variable (default: opensearch_output)
if(this.step.getAttributes().get("opensearch_output_variable") != null){
this.opensearch_output_variable = (String) this.step.getAttributes().get("opensearch_output_variable");
} else {
this.opensearch_output_variable = "opensearch_output";
}
logger.info("opensearch_output_variable: " + this.opensearch_output_variable);
// Load optional Ks* filter fields
// if(this.step.getAttributes().get("opensearch_ks_application_name") != null){
// this.opensearch_ks_application_name = (String) this.step.getAttributes().get("opensearch_ks_application_name");
// logger.info("opensearch_ks_application_name: " + this.opensearch_ks_application_name);
// }
// if(this.step.getAttributes().get("opensearch_ks_doctype") != null){
// this.opensearch_ks_doctype = (String) this.step.getAttributes().get("opensearch_ks_doctype");
// logger.info("opensearch_ks_doctype: " + this.opensearch_ks_doctype);
// }
// if(this.step.getAttributes().get("opensearch_ks_doc_source") != null){
// this.opensearch_ks_doc_source = (String) this.step.getAttributes().get("opensearch_ks_doc_source");
// logger.info("opensearch_ks_doc_source: " + this.opensearch_ks_doc_source);
// }
// if(this.step.getAttributes().get("opensearch_ks_file_source") != null){
// this.opensearch_ks_file_source = (String) this.step.getAttributes().get("opensearch_ks_file_source");
// logger.info("opensearch_ks_file_source: " + this.opensearch_ks_file_source);
// }
// if(this.step.getAttributes().get("opensearch_ks_document_id") != null){
// this.opensearch_ks_document_id = (String) this.step.getAttributes().get("opensearch_ks_document_id");
// logger.info("opensearch_ks_document_id: " + this.opensearch_ks_document_id);
// }
// if(this.step.getAttributes().get("opensearch_ks_project_name") != null){
// this.opensearch_ks_project_name = (String) this.step.getAttributes().get("opensearch_ks_project_name");
// logger.info("opensearch_ks_project_name: " + this.opensearch_ks_project_name);
// }
// if(this.step.getAttributes().get("opensearch_ks_knowledge_path") != null){
// this.opensearch_ks_knowledge_path = (String) this.step.getAttributes().get("opensearch_ks_knowledge_path");
// logger.info("opensearch_ks_knowledge_path: " + this.opensearch_ks_knowledge_path);
// }
// if(this.step.getAttributes().get("opensearch_tags") != null){
// this.opensearch_tags = (String) this.step.getAttributes().get("opensearch_tags");
// logger.info("opensearch_tags: " + this.opensearch_tags);
// }
// Parse variables from execution context
AttributeParser attributeParser = new AttributeParser(this.scenarioExecution);
this.opensearch_query = attributeParser.parse(this.opensearch_query);
// this.opensearch_project = attributeParser.parse(this.opensearch_project);
// if(this.opensearch_ks_application_name != null){
// this.opensearch_ks_application_name = attributeParser.parse(this.opensearch_ks_application_name);
// }
// if(this.opensearch_ks_doctype != null){
// this.opensearch_ks_doctype = attributeParser.parse(this.opensearch_ks_doctype);
// }
// if(this.opensearch_ks_doc_source != null){
// this.opensearch_ks_doc_source = attributeParser.parse(this.opensearch_ks_doc_source);
// }
// if(this.opensearch_ks_file_source != null){
// this.opensearch_ks_file_source = attributeParser.parse(this.opensearch_ks_file_source);
// }
// if(this.opensearch_ks_document_id != null){
// this.opensearch_ks_document_id = attributeParser.parse(this.opensearch_ks_document_id);
// }
// if(this.opensearch_ks_project_name != null){
// this.opensearch_ks_project_name = attributeParser.parse(this.opensearch_ks_project_name);
// }
// if(this.opensearch_ks_knowledge_path != null){
// this.opensearch_ks_knowledge_path = attributeParser.parse(this.opensearch_ks_knowledge_path);
// }
// if(this.opensearch_tags != null){
// this.opensearch_tags = attributeParser.parse(this.opensearch_tags);
// }
}
@Override
public ScenarioExecution solveStep() throws Exception {
logger.info("Solving OpenSearch Query step: " + this.step.getName());
this.scenarioExecution.setCurrentStepId(this.step.getStepId());
loadParameters();
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
String endpoint = this.opensearch_base_url + "/api/agent/search";
JSONObject requestBody = new JSONObject();
// Required fields
// requestBody.put("project", this.opensearch_project);
requestBody.put("query", this.opensearch_query);
requestBody.put("search_type", this.opensearch_search_type);
requestBody.put("k", this.opensearch_k);
requestBody.put("max_context_chars", this.opensearch_max_context_chars);
requestBody.put("vector_weight", this.opensearch_vector_weight);
requestBody.put("entity_weight", this.opensearch_entity_weight);
// Add optional Ks* filters
// if(this.opensearch_ks_application_name != null){
// requestBody.put("KsApplicationName", this.opensearch_ks_application_name);
// }
// if(this.opensearch_ks_doctype != null){
// requestBody.put("KsDoctype", this.opensearch_ks_doctype);
// }
// if(this.opensearch_ks_doc_source != null){
// requestBody.put("KsDocSource", this.opensearch_ks_doc_source);
// }
// if(this.opensearch_ks_file_source != null){
// requestBody.put("KsFileSource", this.opensearch_ks_file_source);
// }
// if(this.opensearch_ks_document_id != null){
// requestBody.put("KsDocumentId", this.opensearch_ks_document_id);
// }
// if(this.opensearch_ks_project_name != null){
// requestBody.put("KsProjectName", this.opensearch_ks_project_name);
// }
// if(this.opensearch_ks_knowledge_path != null){
// requestBody.put("KsKnowledgePath", this.opensearch_ks_knowledge_path);
// }
// // Add tags filter if provided
// if(this.opensearch_tags != null && !this.opensearch_tags.isEmpty()){
// try {
// JSONObject tagsObj = new JSONObject(this.opensearch_tags);
// requestBody.put("tags", tagsObj);
// } catch (Exception e) {
// logger.warn("Failed to parse opensearch_tags as JSON, skipping", e);
// }
// }
logger.info("Calling OpenSearch endpoint: " + endpoint);
logger.info("Request body: " + requestBody.toString(2));
HttpEntity<String> request = new HttpEntity<>(requestBody.toString(), headers);
try {
ResponseEntity<String> response = restTemplate.exchange(
endpoint,
HttpMethod.POST,
request,
String.class
);
JSONObject jsonResponse = new JSONObject(response.getBody());
logger.info("OpenSearch query completed successfully");
logger.info("Total results: " + jsonResponse.optInt("total"));
logger.info("Search type: " + jsonResponse.optString("search_type"));
// Store the complete response
this.scenarioExecution.getExecSharedMap().put(this.opensearch_output_variable, jsonResponse.toString());
// Store formatted context separately for easy access
String formattedContext = jsonResponse.optString("formatted_context", "");
this.scenarioExecution.getExecSharedMap().put(
this.opensearch_output_variable + "_context",
formattedContext
);
// Store just the results array for processing
JSONArray results = jsonResponse.optJSONArray("results");
if(results != null){
this.scenarioExecution.getExecSharedMap().put(
this.opensearch_output_variable + "_results",
results.toString()
);
}
// Move to next step
this.scenarioExecution.setNextStepId(this.step.getNextStepId());
} catch (Exception e) {
logger.error("Error calling OpenSearch service", e);
throw new Exception("OpenSearch query failed: " + e.getMessage());
}
return this.scenarioExecution;
}
}