diff --git a/AGENTS.md b/AGENTS.md index c369c24d13..6016f687a6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -305,7 +305,7 @@ A new `ILifecycleTask` requires ALL of: - [ ] `ExtensionDescriptor` (UI field definitions via `getExtensionDescriptor()`) - [ ] Unit test with Mockito -All task implementations MUST implement: `getId()`, `getType()`, `execute()`, `configure()`, `getExtensionDescriptor()`. +All task implementations MUST implement: `getId()` (returns `TaskId`), `getType()`, `execute()`, `configure()`, `getExtensionDescriptor()`. ### 4.4 Code Patterns @@ -441,7 +441,7 @@ public class MyFeatureTask implements ILifecycleTask { this.dataFactory = dataFactory; } - @Override public String getId() { return ID; } + @Override public TaskId getId() { return new TaskId(ID); } @Override public String getType() { return KEY_MYFEATURE; } @Override @@ -487,7 +487,7 @@ public class MyFeatureTask implements ILifecycleTask { @Override public ExtensionDescriptor getExtensionDescriptor() { - ExtensionDescriptor descriptor = new ExtensionDescriptor(ID); + ExtensionDescriptor descriptor = new ExtensionDescriptor(getId()); descriptor.setDisplayName("My Feature"); ConfigValue uriConfig = new ConfigValue("Resource URI", FieldType.URI, false, null); descriptor.getConfigs().put("uri", uriConfig); diff --git a/docs/architecture.md b/docs/architecture.md index 1830cd22c8..30ec82b518 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -144,7 +144,7 @@ A typical agent lifecycle includes these task types: ```java public interface ILifecycleTask { - String getId(); + TaskId getId(); String getType(); void execute(IConversationMemory memory, Object component) throws LifecycleException; diff --git a/docs/developer-quickstart.md b/docs/developer-quickstart.md index 3888c1651a..3b76abbe8c 100644 --- a/docs/developer-quickstart.md +++ b/docs/developer-quickstart.md @@ -547,8 +547,8 @@ Create a custom lifecycle task: @ApplicationScoped public class MyCustomTask implements ILifecycleTask { @Override - public String getId() { - return "ai.labs.mycompany.customtask"; + public TaskId getId() { + return new TaskId("ai.labs.mycompany.customtask"); } @Override diff --git a/mise.toml b/mise.toml index 5e9510aa9d..6265f44f8d 100644 --- a/mise.toml +++ b/mise.toml @@ -1,23 +1,49 @@ [tools] java = "temurin-25.0.3+9.0.LTS" maven = "3.9.12" +"npm:chromadb" = "3.4.3" -[tasks] -dev = { run = "mvn compile quarkus:dev", description = "Start dev mode with live reload (port 7070)" } -compile = { run = "mvn compile", description = "Compile sources only (fast feedback)" } +[tasks.dev] +depends = ["clean"] +run = "mvn compile quarkus:dev" +description = "Start dev mode with live reload (port 7070)" -clean-build = { run = "mvn clean compile", description = "Clean build — delete target/ and recompile from scratch" } -test = { run = "mvn test", description = "Run unit tests (excludes *IT.java integration tests)" } +[tasks.compile] +depends = ["clean"] +run = "mvn compile" +description = "Compile sources only (fast feedback)" -verify = { run = "mvn verify -DskipITs", description = "Compile + unit tests + package (no integration tests)" } -full-build = { run = "mvn verify", description = "Full build — compile + unit tests + integration tests (requires Docker)" } -validate = { run = "mvn validate", description = "Run Checkstyle code style checks" } -format = { run = "mvn formatter:format", description = "Auto-format Java sources using the project Eclipse formatter" } +[tasks.test] +run = "mvn verify" +description = "Compile + unit tests + integration tests (requires Docker)" -docker-build = { run = "mvn clean package '-Dquarkus.container-image.build=true'", description = "Build the app + Docker image" } -license-gen = { run = "mvn package -Plicense-gen -DskipTests", description = "Generate third-party licenses (Red Hat certification)" } +[tasks.validate] +run = "mvn validate" +description = "Run Checkstyle code style checks" -debug = { run = "mvn quarkus:dev -Dsuspend", description = "Start dev mode and wait for debugger on port 5005" } -dev-no-debug = { run = "mvn quarkus:dev -Ddebug=false", description = "Start dev mode without the debug agent" } -chroma = { run = "docker compose -f docker-compose.chroma.yml up", description = "Starts ChromaDB" } +[tasks.format] +run = "mvn formatter:format" +description = "Auto-format Java sources using the project Eclipse formatter" + +[tasks.docker-build] +run = "mvn clean package '-Dquarkus.container-image.build=true'" +description = "Build the app + Docker image" + + +[tasks.license-gen] +run = "mvn package -Plicense-gen -DskipTests" +description = "Generate third-party licenses (Red Hat certification)" + +[tasks.debug] +run = "mvn quarkus:dev -Dsuspend" +description = "Start dev mode and wait for debugger on port 5005" + +[tasks.clean] +run = "mvn clean" +description = "Clean build — delete target" + + +[tasks.chroma] +run = "docker compose -f docker-compose.chroma.yml up" +description = "Starts ChromaDB" diff --git a/planning/memory-architecture-plan.md b/planning/memory-architecture-plan.md index 91acb9a85f..800e647938 100644 --- a/planning/memory-architecture-plan.md +++ b/planning/memory-architecture-plan.md @@ -327,9 +327,9 @@ Changes required: } catch (Exception e) { // Failure → mark all NEW data written by this task as uncommitted markNewDataUncommitted(memory.getCurrentStep(), preTaskData); - auditLedger.logTaskFailure(task.getId(), e, conversationId); - // Inject error action, NOT the exception, into the pipeline - memory.getCurrentStep().storeData(new Data<>("actions", List.of("task_failed_" + task.getId()))); + auditLedger.logTaskFailure(task.getId().name(), e, conversationId); + // Inject error action, NOT the exception, into the pipeline + memory.getCurrentStep().storeData(new Data<>("actions", List.of("task_failed_" + task.getId().name()))); } ``` diff --git a/planning/observability-and-pipeline-plan.md b/planning/observability-and-pipeline-plan.md index 3984687ff2..e932e3269b 100644 --- a/planning/observability-and-pipeline-plan.md +++ b/planning/observability-and-pipeline-plan.md @@ -31,7 +31,7 @@ The main execution path is in [`LifecycleManager.executeLifecycle()`](../../src/main/java/ai/labs/eddi/engine/lifecycle/LifecycleManager.java). Add span-per-task: ```java -Span span = tracer.spanBuilder("eddi.task." + task.getId()) +Span span = tracer.spanBuilder("eddi.task." + task.getId().name()) .setAttribute("conversationId", conversationId) .setAttribute("agentId", agentId) .startSpan(); diff --git a/src/main/java/ai/labs/eddi/configs/workflows/model/ExtensionDescriptor.java b/src/main/java/ai/labs/eddi/configs/workflows/model/ExtensionDescriptor.java index e7164120e4..fca7e281dc 100644 --- a/src/main/java/ai/labs/eddi/configs/workflows/model/ExtensionDescriptor.java +++ b/src/main/java/ai/labs/eddi/configs/workflows/model/ExtensionDescriptor.java @@ -9,13 +9,16 @@ import java.util.List; import java.util.Map; +import ai.labs.eddi.engine.lifecycle.TaskId; + public class ExtensionDescriptor { - private String type; + private TaskId type; + private String displayName; private Map configs = new HashMap<>(); private Map> extensions = new HashMap<>(); - public ExtensionDescriptor(String type) { + public ExtensionDescriptor(TaskId type) { this.type = type; } @@ -73,11 +76,11 @@ public void addExtension(String extensionName, ExtensionDescriptor extensionDesc extensions.computeIfAbsent(extensionName, k -> new LinkedList<>()).add(extensionDescriptor); } - public String getType() { + public TaskId getType() { return type; } - public void setType(String type) { + public void setType(TaskId type) { this.type = type; } diff --git a/src/main/java/ai/labs/eddi/configs/workflows/rest/RestWorkflowStepStore.java b/src/main/java/ai/labs/eddi/configs/workflows/rest/RestWorkflowStepStore.java index 11dd77aeac..a75e62c216 100644 --- a/src/main/java/ai/labs/eddi/configs/workflows/rest/RestWorkflowStepStore.java +++ b/src/main/java/ai/labs/eddi/configs/workflows/rest/RestWorkflowStepStore.java @@ -14,21 +14,23 @@ import jakarta.inject.Provider; import java.util.List; import java.util.Map; + @ApplicationScoped public class RestWorkflowStepStore implements IRestWorkflowStepStore { private final Map> lifecycleExtensionsProvider; @Inject public RestWorkflowStepStore(@LifecycleExtensions Map> lifecycleExtensionsProvider) { - this.lifecycleExtensionsProvider = lifecycleExtensionsProvider; } @Override public List getWorkflowSteps(String filter) { - return lifecycleExtensionsProvider.keySet().stream().filter(type -> filter.isEmpty() || type.contains(filter)).map(type -> { - Provider taskProvider = lifecycleExtensionsProvider.get(type); - return taskProvider.get().getExtensionDescriptor(); - }).toList(); + return lifecycleExtensionsProvider.keySet().stream() + .filter(type -> filter == null || filter.isEmpty() || type.contains(filter)) + .map(type -> { + Provider taskProvider = lifecycleExtensionsProvider.get(type); + return taskProvider.get().getExtensionDescriptor(); + }).toList(); } } diff --git a/src/main/java/ai/labs/eddi/engine/api/IConversationService.java b/src/main/java/ai/labs/eddi/engine/api/IConversationService.java index 865a8984f8..7d366e1550 100644 --- a/src/main/java/ai/labs/eddi/engine/api/IConversationService.java +++ b/src/main/java/ai/labs/eddi/engine/api/IConversationService.java @@ -8,6 +8,7 @@ import ai.labs.eddi.datastore.IResourceStore.ResourceStoreException; import ai.labs.eddi.engine.memory.model.SimpleConversationMemorySnapshot; import ai.labs.eddi.engine.model.Context; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.memory.model.ConversationState; import ai.labs.eddi.engine.model.Deployment.Environment; import ai.labs.eddi.engine.model.InputData; @@ -105,9 +106,9 @@ interface ConversationResponseHandler { * Callback for streaming conversation responses via SSE. */ interface StreamingResponseHandler { - void onTaskStart(String taskId, String taskType, int index); + void onTaskStart(TaskId taskId, String taskType, int index); - void onTaskComplete(String taskId, String taskType, long durationMs, Map summary); + void onTaskComplete(TaskId taskId, String taskType, long durationMs, Map summary); void onToken(String token); diff --git a/src/main/java/ai/labs/eddi/engine/internal/ConversationService.java b/src/main/java/ai/labs/eddi/engine/internal/ConversationService.java index e35306bb0a..530756431c 100644 --- a/src/main/java/ai/labs/eddi/engine/internal/ConversationService.java +++ b/src/main/java/ai/labs/eddi/engine/internal/ConversationService.java @@ -19,6 +19,7 @@ import ai.labs.eddi.engine.caching.ICacheFactory; import ai.labs.eddi.engine.lifecycle.ConversationEventSink; import ai.labs.eddi.engine.lifecycle.IConversation; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.lifecycle.exceptions.LifecycleException; import ai.labs.eddi.engine.memory.ConversationLogGenerator; import ai.labs.eddi.engine.memory.IConversationMemory; @@ -420,12 +421,12 @@ public void sayStreaming(Environment environment, String agentId, String convers // Create event sink that delegates to the streaming handler var eventSink = new ConversationEventSink() { @Override - public void onTaskStart(String taskId, String taskType, int index) { + public void onTaskStart(TaskId taskId, String taskType, int index) { streamingHandler.onTaskStart(taskId, taskType, index); } @Override - public void onTaskComplete(String taskId, String taskType, long durationMs, Map summary) { + public void onTaskComplete(TaskId taskId, String taskType, long durationMs, Map summary) { streamingHandler.onTaskComplete(taskId, taskType, durationMs, summary); } diff --git a/src/main/java/ai/labs/eddi/engine/internal/RestAgentEngineStreaming.java b/src/main/java/ai/labs/eddi/engine/internal/RestAgentEngineStreaming.java index 0710d8d67a..36e46bd179 100644 --- a/src/main/java/ai/labs/eddi/engine/internal/RestAgentEngineStreaming.java +++ b/src/main/java/ai/labs/eddi/engine/internal/RestAgentEngineStreaming.java @@ -8,6 +8,7 @@ import ai.labs.eddi.engine.api.IRestAgentEngineStreaming; import ai.labs.eddi.engine.memory.model.SimpleConversationMemorySnapshot; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.model.InputData; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -55,15 +56,16 @@ public void sayStreaming(String conversationId, Boolean returnDetailed, Boolean conversationService.sayStreaming(conversationId, returnDetailed, returnCurrentStepOnly, returningFields, inputData, new IConversationService.StreamingResponseHandler() { @Override - public void onTaskStart(String taskId, String taskType, int index) { + public void onTaskStart(TaskId taskId, String taskType, int index) { sendEvent(eventSink, sse, "task_start", - String.format("{\"taskId\":\"%s\",\"taskType\":\"%s\",\"index\":%d}", taskId, taskType, index)); + String.format("{\"taskId\":\"%s\",\"taskType\":\"%s\",\"index\":%d}", taskId.getIdentifier(), taskType, index)); } @Override - public void onTaskComplete(String taskId, String taskType, long durationMs, Map summary) { + public void onTaskComplete(TaskId taskId, String taskType, long durationMs, Map summary) { var sb = new StringBuilder(); - sb.append(String.format("{\"taskId\":\"%s\",\"taskType\":\"%s\",\"durationMs\":%d", taskId, taskType, durationMs)); + sb.append(String.format("{\"taskId\":\"%s\",\"taskType\":\"%s\",\"durationMs\":%d", taskId.getIdentifier(), taskType, + durationMs)); if (summary.containsKey("actions")) { sb.append(",\"actions\":").append(toJsonArray(summary.get("actions"))); } diff --git a/src/main/java/ai/labs/eddi/engine/lifecycle/ConversationEventSink.java b/src/main/java/ai/labs/eddi/engine/lifecycle/ConversationEventSink.java index 01048a6aa2..d2063d3085 100644 --- a/src/main/java/ai/labs/eddi/engine/lifecycle/ConversationEventSink.java +++ b/src/main/java/ai/labs/eddi/engine/lifecycle/ConversationEventSink.java @@ -33,7 +33,7 @@ public interface ConversationEventSink { * @param index * 0-based position in the workflow */ - void onTaskStart(String taskId, String taskType, int index); + void onTaskStart(TaskId taskId, String taskType, int index); /** * Called after a lifecycle task completes execution. @@ -48,7 +48,7 @@ public interface ConversationEventSink { * optional task-specific summary data (e.g. emitted actions). May be * empty, never null. */ - void onTaskComplete(String taskId, String taskType, long durationMs, Map summary); + void onTaskComplete(TaskId taskId, String taskType, long durationMs, Map summary); /** * Called for each LLM token during streaming chat completion. diff --git a/src/main/java/ai/labs/eddi/engine/lifecycle/ILifecycleTask.java b/src/main/java/ai/labs/eddi/engine/lifecycle/ILifecycleTask.java index add277eba7..eb02af297e 100644 --- a/src/main/java/ai/labs/eddi/engine/lifecycle/ILifecycleTask.java +++ b/src/main/java/ai/labs/eddi/engine/lifecycle/ILifecycleTask.java @@ -99,7 +99,7 @@ public interface ILifecycleTask { * @return unique identifier of this task (e.g., "ai.labs.parser", * "ai.labs.behavior") */ - String getId(); + TaskId getId(); /** * Returns the type identifier for this lifecycle task. diff --git a/src/main/java/ai/labs/eddi/engine/lifecycle/TaskId.java b/src/main/java/ai/labs/eddi/engine/lifecycle/TaskId.java new file mode 100644 index 0000000000..5e3e1fecbe --- /dev/null +++ b/src/main/java/ai/labs/eddi/engine/lifecycle/TaskId.java @@ -0,0 +1,122 @@ +/* + * Copyright EDDI contributors + * SPDX-License-Identifier: Apache-2.0 + */ +package ai.labs.eddi.engine.lifecycle; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Immutable identifier for lifecycle tasks in the EDDI conversation pipeline. + * + *

+ * TaskId provides a type-safe wrapper around task identifiers with a canonical + * URI format ({@code eddi://ai.labs.taskname}). It ensures consistent + * identifier handling across the system and provides clean JSON serialization. + *

+ * + *

+ * Usage: + *

+ * + *
{@code
+ * TaskId parserTask = new TaskId("ai.labs.parser");
+ * String identifier = parserTask.getIdentifier(); // "eddi://ai.labs.parser"
+ * String name = parserTask.name(); // "ai.labs.parser"
+ * }
+ * + *

+ * JSON Serialization: + *

+ *
    + *
  • Serialize: {@code "eddi://ai.labs.parser"} (via + * {@link #getIdentifier()})
  • + *
  • Deserialize: Accepts both + * {@code "eddi://ai.labs.parser"} and {@code "ai.labs.parser"}
  • + *
+ * + * @param name + * the task identifier (e.g., "ai.labs.parser", "ai.labs.behavior") + * @author EDDI Team + * @since 6.0 + * @see ILifecycleTask#getId() + */ +public record TaskId(String name) { + + public TaskId { + if (name == null || name.isBlank()) { + throw new IllegalArgumentException("TaskId name cannot be null or blank"); + } + } + + /** + * URI scheme prefix for all task identifiers. Full identifiers have the format + * {@code eddi://}. + */ + private static final String SCHEME = "eddi://"; + + /** + * Returns the full URI identifier for this task. + * + *

+ * This is the canonical string representation used for: + *

    + *
  • JSON serialization (via {@link JsonValue})
  • + *
  • Component cache lookups
  • + *
  • Logging and debugging
  • + *
  • OpenTelemetry span attributes
  • + *
+ *

+ * + * @return the full URI identifier (e.g., "eddi://ai.labs.parser") + */ + @JsonValue + public String getIdentifier() { + return SCHEME + name; + } + + /** + * Returns the full URI identifier. + * + *

+ * Delegates to {@link #getIdentifier()} for consistent string representation. + *

+ * + * @return the full URI identifier + */ + @Override + public String toString() { + return getIdentifier(); + } + + /** + * Factory method for deserializing TaskId from a string value. + * + *

+ * Accepts both formats: + *

    + *
  • Full URI: {@code "eddi://ai.labs.parser"}
  • + *
  • Bare type: {@code "ai.labs.parser"}
  • + *
+ *

+ * + * @param value + * the string to deserialize (must not be null or blank) + * @return a new TaskId instance + * @throws IllegalArgumentException + * if value is null or blank + */ + @JsonCreator + public static TaskId fromValue(String value) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException("TaskId cannot be null or blank"); + } + + if (value.startsWith(SCHEME)) { + return new TaskId(value.substring(SCHEME.length())); + } + + return new TaskId(value); + } +} diff --git a/src/main/java/ai/labs/eddi/engine/lifecycle/internal/LifecycleManager.java b/src/main/java/ai/labs/eddi/engine/lifecycle/internal/LifecycleManager.java index 802d6faf10..860b44d5d2 100644 --- a/src/main/java/ai/labs/eddi/engine/lifecycle/internal/LifecycleManager.java +++ b/src/main/java/ai/labs/eddi/engine/lifecycle/internal/LifecycleManager.java @@ -214,6 +214,11 @@ public void executeLifecycle(final IConversationMemory conversationMemory, List< for (int index = 0; index < lifecycleTasks.size(); index++) { ILifecycleTask task = lifecycleTasks.get(index); + // Fail-fast: every task must have a non-null TaskId + if (task.getId() == null) { + throw new LifecycleException("Lifecycle task returned null TaskId: " + task.getClass().getName()); + } + // Check if execution should be interrupted (graceful shutdown) if (Thread.currentThread().isInterrupted()) { throw new LifecycleException.LifecycleInterruptedException("Execution was interrupted!"); @@ -236,7 +241,7 @@ public void executeLifecycle(final IConversationMemory conversationMemory, List< // === OpenTelemetry: create span per task === Span taskSpan = getTracer().spanBuilder("eddi.pipeline.task") - .setAttribute("eddi.task.id", Objects.requireNonNullElse(task.getId(), "unknown")) + .setAttribute("eddi.task.id", task.getId().name()) .setAttribute("eddi.task.type", Objects.requireNonNullElse(task.getType(), "unknown")) .setAttribute("eddi.task.index", (long) index) .setAttribute("eddi.conversation.id", @@ -251,7 +256,7 @@ public void executeLifecycle(final IConversationMemory conversationMemory, List< // Retrieve task's component from cache // Component contains task-specific configuration loaded during agent // initialization - var components = componentCache.getComponentMap(task.getId()); + var components = componentCache.getComponentMap(task.getId().name()); var componentKey = createComponentKey(workflowId.getId(), workflowId.getVersion(), index); var component = components.getOrDefault(componentKey, null); @@ -286,7 +291,7 @@ public void executeLifecycle(final IConversationMemory conversationMemory, List< taskSpan.recordException(e); // Record error counter for dashboards & alerting - String errTaskId = task.getId() != null ? task.getId() : "unknown"; + String errTaskId = task.getId().name(); String errTaskType = task.getType() != null ? task.getType() : "unknown"; String errMeterKey = errTaskId + "|" + errTaskType; TASK_ERROR_COUNTERS.computeIfAbsent(errMeterKey, k -> Counter.builder("eddi.pipeline.task.errors") @@ -315,7 +320,7 @@ public void executeLifecycle(final IConversationMemory conversationMemory, List< // duration histogram (failing tasks with long timeouts are especially // important for latency monitoring during incidents). long durationMs = (System.nanoTime() - taskStartTime) / 1_000_000; - String taskId = task.getId() != null ? task.getId() : "unknown"; + String taskId = task.getId().name(); String taskType = task.getType() != null ? task.getType() : "unknown"; String meterKey = taskId + "|" + taskType; TASK_TIMERS.computeIfAbsent(meterKey, k -> Timer.builder("eddi.pipeline.task.duration") @@ -342,7 +347,7 @@ private Map buildTaskSummary(IConversationMemory conversationMem summary.put("actions", actionData.getResult()); } // Tool execution trace (for LLM tasks) — enables live tool call display in UI - IData traceData = conversationMemory.getCurrentStep().getLatestData("langchain:trace:" + task.getId()); + IData traceData = conversationMemory.getCurrentStep().getLatestData("langchain:trace:" + task.getId().name()); if (traceData != null && traceData.getResult() != null) { summary.put("toolTrace", traceData.getResult()); } @@ -397,9 +402,11 @@ private AuditEntry buildAuditEntry(IConversationMemory memory, ILifecycleTask ta @SuppressWarnings("unchecked") List actions = summary.containsKey("actions") ? (List) summary.get("actions") : null; - return new AuditEntry(UUID.randomUUID().toString(), memory.getConversationId(), memory.getAgentId(), memory.getAgentVersion(), + return new AuditEntry(UUID.randomUUID().toString(), memory.getConversationId(), memory.getAgentId(), + memory.getAgentVersion(), memory.getUserId(), null, // environment is set by ConversationService - stepIndex, task.getId(), task.getType(), taskIndex, durationMs, input.isEmpty() ? null : input, output.isEmpty() ? null : output, + stepIndex, task.getId().name(), task.getType(), taskIndex, durationMs, input.isEmpty() ? null : input, + output.isEmpty() ? null : output, llmDetail, null, // toolCalls — set by LlmTask in memory actions, 0.0, // cost — set by ToolCostTracker integration Instant.now(), null // HMAC computed by AuditLedgerService @@ -583,7 +590,7 @@ private void injectErrorDigest(ConversationStep step, ILifecycleTask task, Excep // Store under separate "taskErrors" key — never mixed into "output" var errorOutput = new LinkedHashMap(); errorOutput.put("type", "errorDigest"); - errorOutput.put("taskId", task.getId()); + errorOutput.put("taskId", task.getId().name()); errorOutput.put("taskType", task.getType()); errorOutput.put("text", digestText); step.addConversationOutputList("taskErrors", List.of(errorOutput)); @@ -605,7 +612,7 @@ private void injectErrorDigest(ConversationStep step, ILifecycleTask task, Excep */ private void injectFailureAction(ConversationStep step, ILifecycleTask task, List actionsBefore) { - String failureAction = "task_failed_" + task.getId(); + String failureAction = "task_failed_" + task.getId().name(); // Rebuild from pre-failure state + failure action List actions = new ArrayList<>(actionsBefore); diff --git a/src/main/java/ai/labs/eddi/engine/runtime/client/workflows/WorkflowStoreClientLibrary.java b/src/main/java/ai/labs/eddi/engine/runtime/client/workflows/WorkflowStoreClientLibrary.java index 36e5dc8875..84a13c70e2 100644 --- a/src/main/java/ai/labs/eddi/engine/runtime/client/workflows/WorkflowStoreClientLibrary.java +++ b/src/main/java/ai/labs/eddi/engine/runtime/client/workflows/WorkflowStoreClientLibrary.java @@ -84,7 +84,11 @@ private IExecutableWorkflow createExecutableWorkflow(final DocumentDescriptor do var component = lifecycleTask.configure(workflowStep.getConfig(), workflowStep.getExtensions()); if (component != null) { - componentCache.put(lifecycleTask.getId(), componentKey, component); + if (lifecycleTask.getId() == null) { + throw new WorkflowInitializationException( + "Lifecycle task returned null TaskId: " + lifecycleTask.getClass().getName(), null); + } + componentCache.put(lifecycleTask.getId().name(), componentKey, component); } lifecycleManager.addLifecycleTask(lifecycleTask); } diff --git a/src/main/java/ai/labs/eddi/modules/apicalls/impl/ApiCallsTask.java b/src/main/java/ai/labs/eddi/modules/apicalls/impl/ApiCallsTask.java index 33ff99a3b6..32d9f8af83 100644 --- a/src/main/java/ai/labs/eddi/modules/apicalls/impl/ApiCallsTask.java +++ b/src/main/java/ai/labs/eddi/modules/apicalls/impl/ApiCallsTask.java @@ -9,6 +9,7 @@ import ai.labs.eddi.configs.workflows.model.ExtensionDescriptor.ConfigValue; import ai.labs.eddi.configs.workflows.model.ExtensionDescriptor.FieldType; import ai.labs.eddi.engine.lifecycle.ILifecycleTask; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.lifecycle.exceptions.LifecycleException; import ai.labs.eddi.engine.lifecycle.exceptions.WorkflowConfigurationException; import ai.labs.eddi.engine.memory.IConversationMemory; @@ -32,6 +33,8 @@ @ApplicationScoped public class ApiCallsTask implements ILifecycleTask { public static final String ID = "ai.labs.httpcalls"; + public static final TaskId TASK_ID = new TaskId(ID); + private static final String KEY_HTTP_CALLS = "httpCalls"; private final IResourceClientLibrary resourceClientLibrary; private final IMemoryItemConverter memoryItemConverter; @@ -47,8 +50,8 @@ public ApiCallsTask(IResourceClientLibrary resourceClientLibrary, IMemoryItemCon } @Override - public String getId() { - return ID; + public TaskId getId() { + return TASK_ID; } @Override @@ -118,7 +121,7 @@ public Object configure(Map configuration, Map e @Override public ExtensionDescriptor getExtensionDescriptor() { - ExtensionDescriptor extensionDescriptor = new ExtensionDescriptor(ID); + ExtensionDescriptor extensionDescriptor = new ExtensionDescriptor(new TaskId(ID)); extensionDescriptor.setDisplayName("Http Calls"); ConfigValue configValue = new ConfigValue("Resource URI", FieldType.URI, false, null); extensionDescriptor.getConfigs().put("uri", configValue); diff --git a/src/main/java/ai/labs/eddi/modules/llm/impl/LlmTask.java b/src/main/java/ai/labs/eddi/modules/llm/impl/LlmTask.java index e95c8e7a43..c8c253f08d 100644 --- a/src/main/java/ai/labs/eddi/modules/llm/impl/LlmTask.java +++ b/src/main/java/ai/labs/eddi/modules/llm/impl/LlmTask.java @@ -15,6 +15,7 @@ import ai.labs.eddi.engine.attachments.IAttachmentStore; import ai.labs.eddi.engine.lifecycle.ConversationEventSink; import ai.labs.eddi.engine.lifecycle.ILifecycleTask; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.lifecycle.exceptions.LifecycleException; import ai.labs.eddi.engine.lifecycle.exceptions.WorkflowConfigurationException; import ai.labs.eddi.engine.memory.*; @@ -62,6 +63,7 @@ @ApplicationScoped public class LlmTask implements ILifecycleTask { public static final String ID = "ai.labs.llm"; + public static final TaskId TASK_ID = new TaskId(ID); private static final String KEY_URI = "uri"; private static final String KEY_LANGCHAIN = "langchain"; @@ -152,8 +154,8 @@ public LlmTask(IResourceClientLibrary resourceClientLibrary, IDataFactory dataFa } @Override - public String getId() { - return ID; + public TaskId getId() { + return TASK_ID; } @Override @@ -672,7 +674,7 @@ private static String resolveModelName(Map processedParams) { @Override public ExtensionDescriptor getExtensionDescriptor() { - ExtensionDescriptor extensionDescriptor = new ExtensionDescriptor(ID); + ExtensionDescriptor extensionDescriptor = new ExtensionDescriptor(new TaskId(ID)); extensionDescriptor.setDisplayName("Lang Chain"); ConfigValue configValue = new ConfigValue("Resource URI", FieldType.URI, false, null); diff --git a/src/main/java/ai/labs/eddi/modules/mcpcalls/impl/McpCallsTask.java b/src/main/java/ai/labs/eddi/modules/mcpcalls/impl/McpCallsTask.java index 0c235143a7..d675454c8c 100644 --- a/src/main/java/ai/labs/eddi/modules/mcpcalls/impl/McpCallsTask.java +++ b/src/main/java/ai/labs/eddi/modules/mcpcalls/impl/McpCallsTask.java @@ -9,6 +9,7 @@ import ai.labs.eddi.configs.workflows.model.ExtensionDescriptor; import ai.labs.eddi.datastore.serialization.IJsonSerialization; import ai.labs.eddi.engine.lifecycle.ILifecycleTask; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.lifecycle.exceptions.LifecycleException; import ai.labs.eddi.engine.lifecycle.exceptions.WorkflowConfigurationException; import ai.labs.eddi.engine.memory.IConversationMemory; @@ -62,8 +63,9 @@ */ @ApplicationScoped public class McpCallsTask implements ILifecycleTask { - public static final String ID = "ai.labs.mcpcalls"; + public static final TaskId TASK_ID = new TaskId(ID); + private static final String KEY_ACTIONS = "actions"; private static final String KEY_MCP_CALLS = "mcpCalls"; @@ -86,8 +88,8 @@ public McpCallsTask(IResourceClientLibrary resourceClientLibrary, IMemoryItemCon } @Override - public String getId() { - return ID; + public TaskId getId() { + return TASK_ID; } @Override @@ -277,7 +279,7 @@ public Object configure(Map configuration, Map e @Override public ExtensionDescriptor getExtensionDescriptor() { - ExtensionDescriptor descriptor = new ExtensionDescriptor(ID); + ExtensionDescriptor descriptor = new ExtensionDescriptor(new TaskId(ID)); descriptor.setDisplayName("MCP Calls"); descriptor.getConfigs().put("uri", new ExtensionDescriptor.ConfigValue("Resource URI", ExtensionDescriptor.FieldType.URI, false, null)); return descriptor; diff --git a/src/main/java/ai/labs/eddi/modules/nlp/InputParserTask.java b/src/main/java/ai/labs/eddi/modules/nlp/InputParserTask.java index 5be182904f..fa0ec2dc0c 100644 --- a/src/main/java/ai/labs/eddi/modules/nlp/InputParserTask.java +++ b/src/main/java/ai/labs/eddi/modules/nlp/InputParserTask.java @@ -7,6 +7,7 @@ import ai.labs.eddi.configs.workflows.model.ExtensionDescriptor; import ai.labs.eddi.configs.workflows.model.ExtensionDescriptor.ConfigValue; import ai.labs.eddi.engine.lifecycle.ILifecycleTask; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.lifecycle.exceptions.IllegalExtensionConfigurationException; import ai.labs.eddi.engine.lifecycle.exceptions.WorkflowConfigurationException; import ai.labs.eddi.engine.lifecycle.exceptions.UnrecognizedExtensionException; @@ -61,6 +62,8 @@ @ApplicationScoped public class InputParserTask implements ILifecycleTask { public static final String ID = "ai.labs.parser"; + public static final TaskId TASK_ID = new TaskId(ID); + private static final String CONFIG_APPEND_EXPRESSIONS = "appendExpressions"; private static final String CONFIG_INCLUDE_UNUSED = "includeUnused"; private static final String CONFIG_INCLUDE_UNKNOWN = "includeUnknown"; @@ -94,8 +97,8 @@ public InputParserTask(IExpressionProvider expressionProvider, } @Override - public String getId() { - return "ai.labs.parser"; + public TaskId getId() { + return TASK_ID; } @Override @@ -258,11 +261,11 @@ private List> convertObjectToListOfMaps(Object extension) { @Override public ExtensionDescriptor getExtensionDescriptor() { - ExtensionDescriptor extensionDescriptor = new ExtensionDescriptor(ID); + ExtensionDescriptor extensionDescriptor = new ExtensionDescriptor(new TaskId(ID)); extensionDescriptor.setDisplayName("Input Parser"); normalizerProviders.keySet().forEach(type -> { - ExtensionDescriptor normalizerDescriptor = new ExtensionDescriptor(type); + ExtensionDescriptor normalizerDescriptor = new ExtensionDescriptor(new TaskId(type)); Provider normalizerProvider = normalizerProviders.get(type); INormalizerProvider provider = normalizerProvider.get(); normalizerDescriptor.setDisplayName(provider.getDisplayName()); @@ -271,7 +274,7 @@ public ExtensionDescriptor getExtensionDescriptor() { }); dictionaryProviders.keySet().forEach(type -> { - ExtensionDescriptor dictionaryDescriptor = new ExtensionDescriptor(type); + ExtensionDescriptor dictionaryDescriptor = new ExtensionDescriptor(new TaskId(type)); Provider dictionaryProvider = dictionaryProviders.get(type); IDictionaryProvider provider = dictionaryProvider.get(); dictionaryDescriptor.setDisplayName(provider.getDisplayName()); @@ -280,7 +283,7 @@ public ExtensionDescriptor getExtensionDescriptor() { }); correctionProviders.keySet().forEach(type -> { - ExtensionDescriptor correctionsDescriptor = new ExtensionDescriptor(type); + ExtensionDescriptor correctionsDescriptor = new ExtensionDescriptor(new TaskId(type)); Provider correctionProvider = correctionProviders.get(type); ICorrectionProvider provider = correctionProvider.get(); correctionsDescriptor.setDisplayName(provider.getDisplayName()); diff --git a/src/main/java/ai/labs/eddi/modules/output/impl/OutputGenerationTask.java b/src/main/java/ai/labs/eddi/modules/output/impl/OutputGenerationTask.java index dbea81d693..ea6a6c5bdf 100644 --- a/src/main/java/ai/labs/eddi/modules/output/impl/OutputGenerationTask.java +++ b/src/main/java/ai/labs/eddi/modules/output/impl/OutputGenerationTask.java @@ -10,6 +10,7 @@ import ai.labs.eddi.configs.workflows.model.ExtensionDescriptor.ConfigValue; import ai.labs.eddi.configs.workflows.model.ExtensionDescriptor.FieldType; import ai.labs.eddi.engine.lifecycle.ILifecycleTask; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.lifecycle.exceptions.WorkflowConfigurationException; import ai.labs.eddi.engine.memory.IConversationMemory; import ai.labs.eddi.engine.memory.IConversationMemory.IConversationProperties; @@ -47,6 +48,8 @@ @ApplicationScoped public class OutputGenerationTask implements ILifecycleTask { public static final String ID = "ai.labs.output"; + public static final TaskId TASK_ID = new TaskId(ID); + private static final String MEMORY_OUTPUT_IDENTIFIER = "output"; private static final String MEMORY_QUICK_REPLIES_IDENTIFIER = "quickReplies"; private static final String CONTEXT_IDENTIFIER = "context"; @@ -70,8 +73,8 @@ public OutputGenerationTask(IResourceClientLibrary resourceClientLibrary, IDataF } @Override - public String getId() { - return ID; + public TaskId getId() { + return TASK_ID; } @Override @@ -301,7 +304,7 @@ private List convertQuickRepliesConfig(List configQuickR @Override public ExtensionDescriptor getExtensionDescriptor() { - ExtensionDescriptor extensionDescriptor = new ExtensionDescriptor(ID); + ExtensionDescriptor extensionDescriptor = new ExtensionDescriptor(new TaskId(ID)); extensionDescriptor.setDisplayName("Output Generation"); ConfigValue configValue = new ConfigValue("Resource URI", FieldType.URI, false, null); diff --git a/src/main/java/ai/labs/eddi/modules/properties/impl/PropertySetterTask.java b/src/main/java/ai/labs/eddi/modules/properties/impl/PropertySetterTask.java index 13cdaafb49..78756a3d34 100644 --- a/src/main/java/ai/labs/eddi/modules/properties/impl/PropertySetterTask.java +++ b/src/main/java/ai/labs/eddi/modules/properties/impl/PropertySetterTask.java @@ -10,6 +10,7 @@ import ai.labs.eddi.configs.propertysetter.model.PropertySetterConfiguration; import ai.labs.eddi.engine.model.Context; import ai.labs.eddi.engine.lifecycle.ILifecycleTask; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.lifecycle.exceptions.LifecycleException; import ai.labs.eddi.engine.lifecycle.exceptions.WorkflowConfigurationException; import ai.labs.eddi.engine.memory.IConversationMemory; @@ -50,6 +51,8 @@ @ApplicationScoped public class PropertySetterTask implements ILifecycleTask { public static final String ID = "ai.labs.property"; + public static final TaskId TASK_ID = new TaskId(ID); + private static final Logger LOGGER = Logger.getLogger(PropertySetterTask.class); private static final String EXPRESSIONS_PARSED_IDENTIFIER = "expressions:parsed"; private static final String ACTIONS_IDENTIFIER = "actions"; @@ -93,8 +96,8 @@ public PropertySetterTask(IExpressionProvider expressionProvider, IMemoryItemCon } @Override - public String getId() { - return ID; + public TaskId getId() { + return TASK_ID; } @Override @@ -270,7 +273,7 @@ private Expressions extractContextProperties(List> contextDataLis @Override public ExtensionDescriptor getExtensionDescriptor() { - ExtensionDescriptor extensionDescriptor = new ExtensionDescriptor(ID); + ExtensionDescriptor extensionDescriptor = new ExtensionDescriptor(new TaskId(ID)); extensionDescriptor.setDisplayName("Property Extraction"); return extensionDescriptor; } diff --git a/src/main/java/ai/labs/eddi/modules/rules/impl/RulesEvaluationTask.java b/src/main/java/ai/labs/eddi/modules/rules/impl/RulesEvaluationTask.java index 0de15d64e7..02b819f642 100644 --- a/src/main/java/ai/labs/eddi/modules/rules/impl/RulesEvaluationTask.java +++ b/src/main/java/ai/labs/eddi/modules/rules/impl/RulesEvaluationTask.java @@ -8,6 +8,7 @@ import ai.labs.eddi.datastore.serialization.DeserializationException; import ai.labs.eddi.datastore.serialization.IJsonSerialization; import ai.labs.eddi.engine.lifecycle.ILifecycleTask; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.lifecycle.exceptions.LifecycleException; import ai.labs.eddi.engine.lifecycle.exceptions.WorkflowConfigurationException; import ai.labs.eddi.engine.memory.IConversationMemory; @@ -38,6 +39,8 @@ @ApplicationScoped public class RulesEvaluationTask implements ILifecycleTask { public static final String ID = "ai.labs.behavior"; + public static final TaskId TASK_ID = new TaskId(ID); + public static final String BEHAVIOR_RULES_TYPE = "behavior_rules"; private static final String KEY_BEHAVIOR_RULES_SUCCESS = BEHAVIOR_RULES_TYPE + ":success"; private static final String KEY_BEHAVIOR_RULES_DROPPED_SUCCESS = BEHAVIOR_RULES_TYPE + ":droppedSuccess"; @@ -65,8 +68,8 @@ public RulesEvaluationTask(IResourceClientLibrary resourceClientLibrary, IJsonSe } @Override - public String getId() { - return ID; + public TaskId getId() { + return TASK_ID; } @Override @@ -192,7 +195,7 @@ public Object configure(Map configuration, Map e @Override public ExtensionDescriptor getExtensionDescriptor() { - var extensionDescriptor = new ExtensionDescriptor(ID); + var extensionDescriptor = new ExtensionDescriptor(TASK_ID); extensionDescriptor.setDisplayName("Behavior Rules"); var configValue = new ConfigValue("Resource URI", FieldType.URI, false, null); diff --git a/src/main/java/ai/labs/eddi/modules/templating/OutputTemplateTask.java b/src/main/java/ai/labs/eddi/modules/templating/OutputTemplateTask.java index 914387d0d7..0efbbd7bff 100644 --- a/src/main/java/ai/labs/eddi/modules/templating/OutputTemplateTask.java +++ b/src/main/java/ai/labs/eddi/modules/templating/OutputTemplateTask.java @@ -5,6 +5,7 @@ package ai.labs.eddi.modules.templating; import ai.labs.eddi.engine.lifecycle.ILifecycleTask; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.memory.IConversationMemory; import ai.labs.eddi.engine.memory.IConversationMemory.IWritableConversationStep; import ai.labs.eddi.engine.memory.IData; @@ -34,6 +35,8 @@ @ApplicationScoped public class OutputTemplateTask implements ILifecycleTask { public static final String ID = "ai.labs.templating"; + public static final TaskId TASK_ID = new TaskId(ID); + private static final String OUTPUT_HTML = "output:html"; private static final String PRE_TEMPLATED = "preTemplated"; private static final String POST_TEMPLATED = "postTemplated"; @@ -56,8 +59,8 @@ public OutputTemplateTask(ITemplatingEngine templatingEngine, IMemoryItemConvert } @Override - public String getId() { - return "ai.labs.templating"; + public TaskId getId() { + return TASK_ID; } @Override @@ -201,7 +204,7 @@ private void storeTemplatedData(IWritableConversationStep currentStep, String or @Override public ExtensionDescriptor getExtensionDescriptor() { - ExtensionDescriptor extensionDescriptor = new ExtensionDescriptor(ID); + ExtensionDescriptor extensionDescriptor = new ExtensionDescriptor(TASK_ID); extensionDescriptor.setDisplayName("Templating"); return extensionDescriptor; } diff --git a/src/test/java/ai/labs/eddi/engine/internal/RestAgentEngineStreamingExtendedTest.java b/src/test/java/ai/labs/eddi/engine/internal/RestAgentEngineStreamingExtendedTest.java index 265c35c4cb..a055c2e2bc 100644 --- a/src/test/java/ai/labs/eddi/engine/internal/RestAgentEngineStreamingExtendedTest.java +++ b/src/test/java/ai/labs/eddi/engine/internal/RestAgentEngineStreamingExtendedTest.java @@ -5,6 +5,7 @@ package ai.labs.eddi.engine.internal; import ai.labs.eddi.engine.api.IConversationService; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.memory.model.ConversationOutput; import ai.labs.eddi.engine.memory.model.ConversationState; import ai.labs.eddi.engine.memory.model.SimpleConversationMemorySnapshot; @@ -77,7 +78,7 @@ void onTaskStartSendsEvent() throws Exception { invokeSayStreaming(); var handler = captureHandler(); - handler.onTaskStart("task-1", "LlmTask", 0); + handler.onTaskStart(new TaskId("task-1"), "LlmTask", 0); verify(eventBuilder).name("task_start"); verify(eventBuilder).data(eq(String.class), contains("task-1")); @@ -90,7 +91,7 @@ void onTaskCompleteSendsBasicEvent() throws Exception { invokeSayStreaming(); var handler = captureHandler(); - handler.onTaskComplete("task-1", "LlmTask", 150L, Map.of()); + handler.onTaskComplete(new TaskId("task-1"), "LlmTask", 150L, Map.of()); verify(eventBuilder).name("task_complete"); verify(eventBuilder).data(eq(String.class), contains("task-1")); @@ -102,7 +103,7 @@ void onTaskCompleteIncludesActions() throws Exception { invokeSayStreaming(); var handler = captureHandler(); - handler.onTaskComplete("task-1", "RulesEvaluation", 50L, + handler.onTaskComplete(new TaskId("task-1"), "RulesEvaluation", 50L, Map.of("actions", List.of("greet", "respond"))); var dataCaptor = ArgumentCaptor.forClass(String.class); @@ -118,7 +119,7 @@ void onTaskCompleteIncludesToolTrace() throws Exception { invokeSayStreaming(); var handler = captureHandler(); - handler.onTaskComplete("task-1", "LlmTask", 200L, + handler.onTaskComplete(new TaskId("task-1"), "LlmTask", 200L, Map.of("toolTrace", List.of(Map.of("tool", "weather", "duration", 100)))); var dataCaptor = ArgumentCaptor.forClass(String.class); @@ -133,7 +134,7 @@ void onTaskCompleteIncludesConfidence() throws Exception { invokeSayStreaming(); var handler = captureHandler(); - handler.onTaskComplete("task-1", "LlmTask", 100L, + handler.onTaskComplete(new TaskId("task-1"), "LlmTask", 100L, Map.of("confidence", 0.95)); var dataCaptor = ArgumentCaptor.forClass(String.class); @@ -262,7 +263,7 @@ public String toString() { }; // Should not throw — handled gracefully - assertDoesNotThrow(() -> handler.onTaskComplete("t1", "LlmTask", 100L, + assertDoesNotThrow(() -> handler.onTaskComplete(new TaskId("ai.test.failure"), "LlmTask", 100L, Map.of("toolTrace", circular))); } } diff --git a/src/test/java/ai/labs/eddi/engine/lifecycle/LifecycleManagerTest.java b/src/test/java/ai/labs/eddi/engine/lifecycle/LifecycleManagerTest.java index e54108b857..b0240dae3b 100644 --- a/src/test/java/ai/labs/eddi/engine/lifecycle/LifecycleManagerTest.java +++ b/src/test/java/ai/labs/eddi/engine/lifecycle/LifecycleManagerTest.java @@ -40,6 +40,8 @@ public void setUp() { public void testExecuteLifecycle() throws Exception { // setup ILifecycleTask lifecycleTask = mock(ILifecycleTask.class); + when(lifecycleTask.getId()).thenReturn(new TaskId("test")); + when(lifecycleTask.getType()).thenReturn("test"); lifecycleManager.addLifecycleTask(lifecycleTask); // test @@ -71,11 +73,11 @@ public void testExecuteLifecycle_FilterByType_MatchesPrefix() throws Exception { // Setup: tasks with types "behavior_rules" and "langchain" ILifecycleTask behaviorTask = mock(ILifecycleTask.class); when(behaviorTask.getType()).thenReturn("behavior_rules"); - when(behaviorTask.getId()).thenReturn("behavior_rules"); + when(behaviorTask.getId()).thenReturn(new TaskId("behavior_rules")); ILifecycleTask langchainTask = mock(ILifecycleTask.class); when(langchainTask.getType()).thenReturn("langchain"); - when(langchainTask.getId()).thenReturn("langchain"); + when(langchainTask.getId()).thenReturn(new TaskId("langchain")); lifecycleManager.addLifecycleTask(behaviorTask); lifecycleManager.addLifecycleTask(langchainTask); @@ -93,11 +95,11 @@ public void testExecuteLifecycle_FilterByType_PrefixMatchWorks() throws Exceptio // Setup: task with type "behavior_rules" ILifecycleTask behaviorTask = mock(ILifecycleTask.class); when(behaviorTask.getType()).thenReturn("behavior_rules"); - when(behaviorTask.getId()).thenReturn("behavior_rules"); + when(behaviorTask.getId()).thenReturn(new TaskId("behavior_rules")); ILifecycleTask outputTask = mock(ILifecycleTask.class); when(outputTask.getType()).thenReturn("output"); - when(outputTask.getId()).thenReturn("output"); + when(outputTask.getId()).thenReturn(new TaskId("output")); lifecycleManager.addLifecycleTask(behaviorTask); lifecycleManager.addLifecycleTask(outputTask); @@ -117,7 +119,7 @@ public void testExecuteLifecycle_FilterByType_DoesNotMatchReversed() throws Exce // because "behavior_rules".startsWith("behavior_rules_extended") is false ILifecycleTask behaviorTask = mock(ILifecycleTask.class); when(behaviorTask.getType()).thenReturn("behavior_rules"); - when(behaviorTask.getId()).thenReturn("behavior_rules"); + when(behaviorTask.getId()).thenReturn(new TaskId("behavior_rules")); lifecycleManager.addLifecycleTask(behaviorTask); @@ -132,11 +134,11 @@ public void testExecuteLifecycle_FilterByType_DoesNotMatchReversed() throws Exce public void testExecuteLifecycle_NoFilter_ExecutesAll() throws Exception { ILifecycleTask task1 = mock(ILifecycleTask.class); when(task1.getType()).thenReturn("parser"); - when(task1.getId()).thenReturn("parser"); + when(task1.getId()).thenReturn(new TaskId("parser")); ILifecycleTask task2 = mock(ILifecycleTask.class); when(task2.getType()).thenReturn("output"); - when(task2.getId()).thenReturn("output"); + when(task2.getId()).thenReturn(new TaskId("output")); lifecycleManager.addLifecycleTask(task1); lifecycleManager.addLifecycleTask(task2); @@ -152,7 +154,7 @@ public void testExecuteLifecycle_NoFilter_ExecutesAll() throws Exception { public void testExecuteLifecycle_EmptyFilter_ExecutesAll() throws Exception { ILifecycleTask task = mock(ILifecycleTask.class); when(task.getType()).thenReturn("parser"); - when(task.getId()).thenReturn("parser"); + when(task.getId()).thenReturn(new TaskId("parser")); lifecycleManager.addLifecycleTask(task); diff --git a/src/test/java/ai/labs/eddi/engine/lifecycle/internal/LifecycleManagerStreamingTest.java b/src/test/java/ai/labs/eddi/engine/lifecycle/internal/LifecycleManagerStreamingTest.java index b652341b57..4dfbb2fa7a 100644 --- a/src/test/java/ai/labs/eddi/engine/lifecycle/internal/LifecycleManagerStreamingTest.java +++ b/src/test/java/ai/labs/eddi/engine/lifecycle/internal/LifecycleManagerStreamingTest.java @@ -6,6 +6,7 @@ import ai.labs.eddi.datastore.IResourceStore; import ai.labs.eddi.engine.lifecycle.ConversationEventSink; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.lifecycle.IComponentCache; import ai.labs.eddi.engine.lifecycle.ILifecycleTask; import ai.labs.eddi.engine.lifecycle.exceptions.LifecycleException; @@ -52,11 +53,11 @@ void setUp() throws Exception { eventSink = mock(ConversationEventSink.class); task1 = mock(ILifecycleTask.class); - when(task1.getId()).thenReturn("ai.labs.parser"); + when(task1.getId()).thenReturn(new TaskId("ai.labs.parser")); when(task1.getType()).thenReturn("expressions"); task2 = mock(ILifecycleTask.class); - when(task2.getId()).thenReturn("ai.labs.behavior"); + when(task2.getId()).thenReturn(new TaskId("ai.labs.behavior")); when(task2.getType()).thenReturn("behavior_rules"); lifecycleManager.addLifecycleTask(task1); @@ -71,12 +72,12 @@ void executeLifecycle_withEventSink_emitsEvents() throws Exception { lifecycleManager.executeLifecycle(memory, null); // task_start for each task - verify(eventSink).onTaskStart("ai.labs.parser", "expressions", 0); - verify(eventSink).onTaskStart("ai.labs.behavior", "behavior_rules", 1); + verify(eventSink).onTaskStart(new TaskId("ai.labs.parser"), "expressions", 0); + verify(eventSink).onTaskStart(new TaskId("ai.labs.behavior"), "behavior_rules", 1); // task_complete for each task - verify(eventSink).onTaskComplete(eq("ai.labs.parser"), eq("expressions"), anyLong(), any()); - verify(eventSink).onTaskComplete(eq("ai.labs.behavior"), eq("behavior_rules"), anyLong(), any()); + verify(eventSink).onTaskComplete(eq(new TaskId("ai.labs.parser")), eq("expressions"), anyLong(), any()); + verify(eventSink).onTaskComplete(eq(new TaskId("ai.labs.behavior")), eq("behavior_rules"), anyLong(), any()); // Tasks are still executed verify(task1).execute(eq(memory), any()); @@ -108,14 +109,14 @@ void executeLifecycle_eventsInOrder() throws Exception { lifecycleManager.executeLifecycle(memory, null); // Task 1: start → execute → complete - inOrder.verify(eventSink).onTaskStart("ai.labs.parser", "expressions", 0); + inOrder.verify(eventSink).onTaskStart(new TaskId("ai.labs.parser"), "expressions", 0); inOrder.verify(task1).execute(eq(memory), any()); - inOrder.verify(eventSink).onTaskComplete(eq("ai.labs.parser"), eq("expressions"), anyLong(), any()); + inOrder.verify(eventSink).onTaskComplete(eq(new TaskId("ai.labs.parser")), eq("expressions"), anyLong(), any()); // Task 2: start → execute → complete - inOrder.verify(eventSink).onTaskStart("ai.labs.behavior", "behavior_rules", 1); + inOrder.verify(eventSink).onTaskStart(new TaskId("ai.labs.behavior"), "behavior_rules", 1); inOrder.verify(task2).execute(eq(memory), any()); - inOrder.verify(eventSink).onTaskComplete(eq("ai.labs.behavior"), eq("behavior_rules"), anyLong(), any()); + inOrder.verify(eventSink).onTaskComplete(eq(new TaskId("ai.labs.behavior")), eq("behavior_rules"), anyLong(), any()); } @Test @@ -125,7 +126,7 @@ void executeLifecycle_durationIsNonNegative() throws Exception { lifecycleManager.executeLifecycle(memory, null); - verify(eventSink, times(2)).onTaskComplete(anyString(), anyString(), longThat(duration -> duration >= 0), any()); + verify(eventSink, times(2)).onTaskComplete(any(TaskId.class), anyString(), longThat(duration -> duration >= 0), any()); } @Test @@ -137,8 +138,8 @@ void executeLifecycle_taskThrows_exceptionPropagates() throws Exception { assertThrows(LifecycleException.class, () -> lifecycleManager.executeLifecycle(memory, null)); // task_start should have been emitted before the error - verify(eventSink).onTaskStart("ai.labs.parser", "expressions", 0); + verify(eventSink).onTaskStart(new TaskId("ai.labs.parser"), "expressions", 0); // task_complete should NOT have been emitted for the failed task - verify(eventSink, never()).onTaskComplete(eq("ai.labs.parser"), anyString(), anyLong(), any()); + verify(eventSink, never()).onTaskComplete(eq(new TaskId("ai.labs.parser")), anyString(), anyLong(), any()); } } diff --git a/src/test/java/ai/labs/eddi/engine/lifecycle/internal/LifecycleManagerTest.java b/src/test/java/ai/labs/eddi/engine/lifecycle/internal/LifecycleManagerTest.java index 2f90e1bd80..4e0a2c962e 100644 --- a/src/test/java/ai/labs/eddi/engine/lifecycle/internal/LifecycleManagerTest.java +++ b/src/test/java/ai/labs/eddi/engine/lifecycle/internal/LifecycleManagerTest.java @@ -8,6 +8,7 @@ import ai.labs.eddi.datastore.IResourceStore; import ai.labs.eddi.engine.lifecycle.IComponentCache; import ai.labs.eddi.engine.lifecycle.IConversation; +import ai.labs.eddi.engine.lifecycle.TaskId; import ai.labs.eddi.engine.lifecycle.ILifecycleTask; import ai.labs.eddi.engine.lifecycle.exceptions.ConversationStopException; import ai.labs.eddi.engine.lifecycle.exceptions.LifecycleException; @@ -59,7 +60,7 @@ void addNull() { @DisplayName("valid task is accepted") void addValidTask() { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("task1"); + when(task.getId()).thenReturn(new TaskId("task1")); assertDoesNotThrow(() -> lifecycleManager.addLifecycleTask(task)); } } @@ -86,7 +87,7 @@ void emptyTaskList() { @DisplayName("single task executes successfully") void singleTask() throws Exception { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("parser"); + when(task.getId()).thenReturn(new TaskId("parser")); when(task.getType()).thenReturn("input"); lifecycleManager.addLifecycleTask(task); @@ -108,11 +109,11 @@ void singleTask() throws Exception { @DisplayName("multiple tasks execute in order") void multipleTasks() throws Exception { var task1 = mock(ILifecycleTask.class); - when(task1.getId()).thenReturn("parser"); + when(task1.getId()).thenReturn(new TaskId("parser")); when(task1.getType()).thenReturn("input"); var task2 = mock(ILifecycleTask.class); - when(task2.getId()).thenReturn("behavior"); + when(task2.getId()).thenReturn(new TaskId("behavior")); when(task2.getType()).thenReturn("behavior_rules"); lifecycleManager.addLifecycleTask(task1); @@ -137,7 +138,7 @@ void multipleTasks() throws Exception { @DisplayName("STOP_CONVERSATION action throws ConversationStopException") void stopConversation() throws Exception { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("behavior"); + when(task.getId()).thenReturn(new TaskId("behavior")); when(task.getType()).thenReturn("behavior_rules"); lifecycleManager.addLifecycleTask(task); @@ -163,7 +164,7 @@ void stopConversation() throws Exception { @DisplayName("task failure propagates LifecycleException") void taskFailure() throws Exception { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("llm"); + when(task.getId()).thenReturn(new TaskId("llm")); when(task.getType()).thenReturn("langchain"); doThrow(new LifecycleException("LLM failed")) @@ -187,7 +188,7 @@ void taskFailure() throws Exception { @DisplayName("RuntimeException in task is wrapped in LifecycleException") void runtimeException() throws Exception { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("broken"); + when(task.getId()).thenReturn(new TaskId("broken")); when(task.getType()).thenReturn("custom"); doThrow(new RuntimeException("NPE")) @@ -216,15 +217,15 @@ class SelectiveExecutionTests { @DisplayName("filter by type — only matching and subsequent tasks execute") void filterByType() throws Exception { var parser = mock(ILifecycleTask.class); - when(parser.getId()).thenReturn("parser"); + when(parser.getId()).thenReturn(new TaskId("parser")); when(parser.getType()).thenReturn("input"); var behavior = mock(ILifecycleTask.class); - when(behavior.getId()).thenReturn("behavior"); + when(behavior.getId()).thenReturn(new TaskId("behavior")); when(behavior.getType()).thenReturn("behavior_rules"); var output = mock(ILifecycleTask.class); - when(output.getId()).thenReturn("output"); + when(output.getId()).thenReturn(new TaskId("output")); when(output.getType()).thenReturn("output"); lifecycleManager.addLifecycleTask(parser); @@ -251,7 +252,7 @@ void filterByType() throws Exception { @DisplayName("filter with no match — no tasks execute") void filterNoMatch() throws Exception { var parser = mock(ILifecycleTask.class); - when(parser.getId()).thenReturn("parser"); + when(parser.getId()).thenReturn(new TaskId("parser")); when(parser.getType()).thenReturn("input"); lifecycleManager.addLifecycleTask(parser); @@ -274,7 +275,7 @@ class StrictWriteDisciplineTests { @DisplayName("task failure with strict write enabled — uncommits data and injects error digest") void taskFailureWithStrictWrite() throws Exception { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("llm_task"); + when(task.getId()).thenReturn(new TaskId("llm_task")); when(task.getType()).thenReturn("langchain"); doThrow(new LifecycleException("API timeout")) @@ -316,7 +317,7 @@ void taskFailureWithStrictWrite() throws Exception { @DisplayName("task failure with strict write exclude_all — no digest, but data uncommitted") void taskFailureExcludeAll() throws Exception { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("api_task"); + when(task.getId()).thenReturn(new TaskId("api_task")); when(task.getType()).thenReturn("httpcalls"); doThrow(new LifecycleException("API error")) @@ -360,7 +361,7 @@ class EventSinkTests { @DisplayName("event sink receives task_start and task_complete events") void eventSinkNotified() throws Exception { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("parser"); + when(task.getId()).thenReturn(new TaskId("parser")); when(task.getType()).thenReturn("input"); lifecycleManager.addLifecycleTask(task); @@ -378,8 +379,8 @@ void eventSinkNotified() throws Exception { lifecycleManager.executeLifecycle(memory, null); - verify(eventSink).onTaskStart("parser", "input", 0); - verify(eventSink).onTaskComplete(eq("parser"), eq("input"), anyLong(), anyMap()); + verify(eventSink).onTaskStart(new TaskId("parser"), "input", 0); + verify(eventSink).onTaskComplete(eq(new TaskId("parser")), eq("input"), anyLong(), anyMap()); } } @@ -391,7 +392,7 @@ class AuditCollectorTests { @DisplayName("audit collector receives audit entry when set") void auditCollectorNotified() throws Exception { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("behavior"); + when(task.getId()).thenReturn(new TaskId("behavior")); when(task.getType()).thenReturn("behavior_rules"); lifecycleManager.addLifecycleTask(task); @@ -423,7 +424,7 @@ class ThreadInterruptionTests { @DisplayName("interrupted thread throws LifecycleInterruptedException") void interruptedThread() { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("parser"); + when(task.getId()).thenReturn(new TaskId("parser")); when(task.getType()).thenReturn("input"); lifecycleManager.addLifecycleTask(task); @@ -453,7 +454,7 @@ class StrictWriteDisciplineEdgeCases { @DisplayName("keep_all mode — isEffectivelyEnabled() returns false, no SWD applied") void keepAllMode() throws Exception { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("llm_task"); + when(task.getId()).thenReturn(new TaskId("llm_task")); when(task.getType()).thenReturn("langchain"); doThrow(new LifecycleException("LLM error")) @@ -488,7 +489,7 @@ void keepAllMode() throws Exception { @DisplayName("null memoryPolicy — strict write discipline not applied") void nullMemoryPolicy() throws Exception { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("failing_task"); + when(task.getId()).thenReturn(new TaskId("failing_task")); when(task.getType()).thenReturn("custom"); doThrow(new LifecycleException("fail")) @@ -516,7 +517,7 @@ void nullMemoryPolicy() throws Exception { @DisplayName("disabled strict write — no uncommit even on failure") void disabledStrictWrite() throws Exception { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("failing_task"); + when(task.getId()).thenReturn(new TaskId("failing_task")); when(task.getType()).thenReturn("custom"); doThrow(new LifecycleException("fail")) @@ -549,11 +550,11 @@ void disabledStrictWrite() throws Exception { @DisplayName("failure after first task — second task does NOT execute") void failureStopsPipeline() throws Exception { var task1 = mock(ILifecycleTask.class); - when(task1.getId()).thenReturn("parser"); + when(task1.getId()).thenReturn(new TaskId("parser")); when(task1.getType()).thenReturn("input"); var task2 = mock(ILifecycleTask.class); - when(task2.getId()).thenReturn("behavior"); + when(task2.getId()).thenReturn(new TaskId("behavior")); when(task2.getType()).thenReturn("behavior_rules"); doThrow(new LifecycleException("parse error")) @@ -586,7 +587,7 @@ class SelectiveExecutionAdditionalTests { @DisplayName("empty lifecycleTaskTypes — all tasks execute (same as null)") void emptyListExecutesAll() throws Exception { var task = mock(ILifecycleTask.class); - when(task.getId()).thenReturn("parser"); + when(task.getId()).thenReturn(new TaskId("parser")); when(task.getType()).thenReturn("input"); lifecycleManager.addLifecycleTask(task); diff --git a/src/test/java/ai/labs/eddi/modules/apicalls/impl/ApiCallsTaskTest.java b/src/test/java/ai/labs/eddi/modules/apicalls/impl/ApiCallsTaskTest.java index 637b42f54f..e7ff91cc4a 100644 --- a/src/test/java/ai/labs/eddi/modules/apicalls/impl/ApiCallsTaskTest.java +++ b/src/test/java/ai/labs/eddi/modules/apicalls/impl/ApiCallsTaskTest.java @@ -62,7 +62,7 @@ void setUp() { @Test @DisplayName("returns correct ID") void getId() { - assertEquals("ai.labs.httpcalls", task.getId()); + assertEquals("ai.labs.httpcalls", task.getId().name()); } @Test @@ -315,7 +315,7 @@ void emptyTargetServerUrl() throws Exception { @DisplayName("extension descriptor has correct ID and display name") void extensionDescriptor() { ExtensionDescriptor descriptor = task.getExtensionDescriptor(); - assertEquals("ai.labs.httpcalls", descriptor.getType()); + assertEquals("ai.labs.httpcalls", descriptor.getType().name()); assertEquals("Http Calls", descriptor.getDisplayName()); assertTrue(descriptor.getConfigs().containsKey("uri")); } diff --git a/src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskTest.java b/src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskTest.java index 41fab8baae..6620d1555d 100644 --- a/src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskTest.java +++ b/src/test/java/ai/labs/eddi/modules/llm/impl/LlmTaskTest.java @@ -300,7 +300,7 @@ void testGetExtensionDescriptor() { assertNotNull(descriptor, "The returned ExtensionDescriptor should not be null."); // Assuming ID and Display Name are known and static for the LangChainTask - assertEquals(LlmTask.ID, descriptor.getType(), "The ID should match the expected value."); + assertEquals(LlmTask.ID, descriptor.getType().name(), "The ID should match the expected value."); assertEquals("Lang Chain", descriptor.getDisplayName(), "The display name should match 'Lang Chain'."); assertFalse(descriptor.getConfigs().isEmpty(), "Expected configs to be defined."); @@ -595,7 +595,7 @@ class TaskIdentityTests { @Test @DisplayName("getId should return correct identifier") void testGetId() { - assertEquals("ai.labs.llm", langChainTask.getId()); + assertEquals("ai.labs.llm", langChainTask.getId().name()); } @Test diff --git a/src/test/java/ai/labs/eddi/modules/mcpcalls/McpCallsTaskTest.java b/src/test/java/ai/labs/eddi/modules/mcpcalls/McpCallsTaskTest.java index 6c83b4e5c4..8c81fa7916 100644 --- a/src/test/java/ai/labs/eddi/modules/mcpcalls/McpCallsTaskTest.java +++ b/src/test/java/ai/labs/eddi/modules/mcpcalls/McpCallsTaskTest.java @@ -62,7 +62,7 @@ void setUp() { @Test @DisplayName("getId returns correct identifier") void testGetId() { - assertEquals("ai.labs.mcpcalls", task.getId()); + assertEquals("ai.labs.mcpcalls", task.getId().name()); } @Test @@ -271,7 +271,7 @@ void testExtensionDescriptor() { var descriptor = task.getExtensionDescriptor(); assertNotNull(descriptor); - assertEquals("ai.labs.mcpcalls", descriptor.getType()); + assertEquals("ai.labs.mcpcalls", descriptor.getType().name()); assertEquals("MCP Calls", descriptor.getDisplayName()); assertTrue(descriptor.getConfigs().containsKey("uri")); } diff --git a/src/test/java/ai/labs/eddi/modules/nlp/InputParserTaskTest.java b/src/test/java/ai/labs/eddi/modules/nlp/InputParserTaskTest.java index 34097dec28..9cd85d76bd 100644 --- a/src/test/java/ai/labs/eddi/modules/nlp/InputParserTaskTest.java +++ b/src/test/java/ai/labs/eddi/modules/nlp/InputParserTaskTest.java @@ -69,7 +69,7 @@ class IdentityTests { @Test @DisplayName("getId returns correct identifier") void testGetId() { - assertEquals("ai.labs.parser", task.getId()); + assertEquals("ai.labs.parser", task.getId().name()); } @Test @@ -348,7 +348,7 @@ void testExtensionDescriptor() { var descriptor = task.getExtensionDescriptor(); assertNotNull(descriptor); - assertEquals("ai.labs.parser", descriptor.getType()); + assertEquals("ai.labs.parser", descriptor.getType().name()); assertEquals("Input Parser", descriptor.getDisplayName()); } diff --git a/src/test/java/ai/labs/eddi/modules/output/OutputGenerationTaskTest.java b/src/test/java/ai/labs/eddi/modules/output/OutputGenerationTaskTest.java index f157f53526..1c5ebf4dfc 100644 --- a/src/test/java/ai/labs/eddi/modules/output/OutputGenerationTaskTest.java +++ b/src/test/java/ai/labs/eddi/modules/output/OutputGenerationTaskTest.java @@ -64,7 +64,7 @@ void setUp() { @Test @DisplayName("getId returns correct identifier") void testGetId() { - assertEquals("ai.labs.output", task.getId()); + assertEquals("ai.labs.output", task.getId().name()); } @Test @@ -201,7 +201,7 @@ void testExtensionDescriptor() { var descriptor = task.getExtensionDescriptor(); assertNotNull(descriptor); - assertEquals("ai.labs.output", descriptor.getType()); + assertEquals("ai.labs.output", descriptor.getType().name()); assertEquals("Output Generation", descriptor.getDisplayName()); assertTrue(descriptor.getConfigs().containsKey("uri")); } diff --git a/src/test/java/ai/labs/eddi/modules/properties/PropertySetterTaskTest.java b/src/test/java/ai/labs/eddi/modules/properties/PropertySetterTaskTest.java index 8b3d8d5d3f..7caea8ddcc 100644 --- a/src/test/java/ai/labs/eddi/modules/properties/PropertySetterTaskTest.java +++ b/src/test/java/ai/labs/eddi/modules/properties/PropertySetterTaskTest.java @@ -107,7 +107,7 @@ class IdentityTests { @Test @DisplayName("getId should return correct identifier") void testGetId() { - assertEquals("ai.labs.property", propertySetterTask.getId()); + assertEquals("ai.labs.property", propertySetterTask.getId().name()); } @Test @@ -534,8 +534,8 @@ void testExtensionDescriptor() { var descriptor = propertySetterTask.getExtensionDescriptor(); assertNotNull(descriptor); - assertEquals("ai.labs.property", descriptor.getType()); + assertEquals("ai.labs.property", descriptor.getType().name()); assertEquals("Property Extraction", descriptor.getDisplayName()); } } -} \ No newline at end of file +} diff --git a/src/test/java/ai/labs/eddi/modules/properties/impl/PropertySetterTaskTest.java b/src/test/java/ai/labs/eddi/modules/properties/impl/PropertySetterTaskTest.java index 98f3e5a9e9..4281a51e09 100644 --- a/src/test/java/ai/labs/eddi/modules/properties/impl/PropertySetterTaskTest.java +++ b/src/test/java/ai/labs/eddi/modules/properties/impl/PropertySetterTaskTest.java @@ -58,7 +58,7 @@ void setUp() { @Test @DisplayName("getId returns correct ID") void getId() { - assertEquals("ai.labs.property", task.getId()); + assertEquals("ai.labs.property", task.getId().name()); } @Test diff --git a/src/test/java/ai/labs/eddi/modules/rules/impl/RulesEvaluationTaskTest.java b/src/test/java/ai/labs/eddi/modules/rules/impl/RulesEvaluationTaskTest.java index 1f5f4b0155..05ef5875a2 100644 --- a/src/test/java/ai/labs/eddi/modules/rules/impl/RulesEvaluationTaskTest.java +++ b/src/test/java/ai/labs/eddi/modules/rules/impl/RulesEvaluationTaskTest.java @@ -60,7 +60,7 @@ class IdentityTests { @Test @DisplayName("getId should return correct identifier") void testGetId() { - assertEquals("ai.labs.behavior", task.getId()); + assertEquals("ai.labs.behavior", task.getId().name()); } @Test @@ -247,7 +247,7 @@ void testExtensionDescriptor() { ExtensionDescriptor descriptor = task.getExtensionDescriptor(); assertNotNull(descriptor); - assertEquals("ai.labs.behavior", descriptor.getType()); + assertEquals("ai.labs.behavior", descriptor.getType().name()); assertEquals("Behavior Rules", descriptor.getDisplayName()); assertTrue(descriptor.getConfigs().containsKey("uri"));