Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

package org.elasticsoftware.akces.agentic.beans;

import com.embabel.agent.core.Agent;
import com.embabel.agent.core.AgentPlatform;
import org.elasticsoftware.akces.agentic.AgenticAggregateRuntime;
import org.elasticsoftware.akces.agentic.runtime.AgenticCommandHandlerFunctionAdapter;
Expand Down Expand Up @@ -76,10 +75,9 @@
* Embabel agent platform.</p>
*
* <p>When {@code agentHandledCommands} or {@code agentHandledEvents} are declared, the
* factory looks up an {@link Agent} bean named {@code {aggregateName}Agent} from the
* {@link ApplicationContext}. This {@link Agent} is provided by the implementing
* application and must be registered as a Spring bean before this factory is invoked.
* A fatal error is raised if the bean cannot be found.</p>
* factory creates adapter instances that resolve the matching agent at runtime from
* the {@link AgentPlatform} by aggregate name. No eager bean lookup is performed at
* startup — agent resolution is deferred to command/event processing time.</p>
*
* <p>Produces a {@link KafkaAgenticAggregateRuntime} wrapping the internally built
* {@link KafkaAggregateRuntime}, adding memory-aware and agent-platform operations.</p>
Expand Down Expand Up @@ -160,30 +158,6 @@ private AgentPlatform resolveAgentPlatform(String aggregateName) {
}
}

/**
* Resolves the {@link Agent} for this aggregate from the {@link ApplicationContext}.
*
* <p>Looks for a Spring bean of type {@link Agent} named {@code {aggregateName}Agent}.
* The implementing application is responsible for registering this bean. If no such
* bean is found, a fatal error is raised.
*
* @param aggregateName the aggregate name used to derive the agent bean name
* @return the resolved {@link Agent}; never {@code null}
* @throws IllegalStateException if no matching {@link Agent} bean is found
*/
private Agent resolveAgent(String aggregateName) {
String agentBeanName = aggregateName + "Agent";
try {
return applicationContext.getBean(agentBeanName, Agent.class);
} catch (BeansException e) {
throw new IllegalStateException(
"No Agent bean found with name '" + agentBeanName + "' for agentic aggregate '"
+ aggregateName + "'. "
+ "The implementing application must provide a Spring bean of type "
+ "com.embabel.agent.core.Agent named '" + agentBeanName + "'.", e);
}
}

@SuppressWarnings("unchecked")
private KafkaAggregateRuntime createRuntime(AgenticAggregateInfo agenticInfo,
AgenticAggregate<S> aggregate,
Expand Down Expand Up @@ -295,22 +269,17 @@ private KafkaAggregateRuntime createRuntime(AgenticAggregateInfo agenticInfo,
buildAgentProducedErrorTypes(agenticInfo.agentProducedErrors());
agentProducedErrorTypes.forEach(runtimeBuilder::addDomainEvent);

// Lazy Agent resolution: only needed when agent-handled commands or events exist.
boolean hasAgentHandlers = agenticInfo.agentHandledCommands().length > 0
|| agenticInfo.agentHandledEvents().length > 0;
Agent agent = hasAgentHandlers ? resolveAgent(agenticInfo.value()) : null;

// Process agentHandledCommands — register AgenticCommandHandlerFunctionAdapter
for (Class<? extends Command> commandClass : agenticInfo.agentHandledCommands()) {
processAgentHandledCommand(
commandClass, aggregate, agentPlatform, agent,
commandClass, aggregate, agenticInfo.value(), agentPlatform,
agentProducedErrorTypes, runtimeBuilder);
}

// Process agentHandledEvents — register AgenticEventHandlerFunctionAdapter
for (Class<? extends DomainEvent> eventClass : agenticInfo.agentHandledEvents()) {
processAgentHandledEvent(
eventClass, aggregate, agentPlatform, agent,
eventClass, aggregate, agenticInfo.value(), agentPlatform,
agentProducedErrorTypes, runtimeBuilder);
}

Expand Down Expand Up @@ -352,17 +321,17 @@ private List<DomainEventType<?>> buildAgentProducedErrorTypes(
*
* @param commandClass the command class to register
* @param aggregate the owning aggregate instance
* @param aggregateName the aggregate name used for agent inference at runtime
* @param agentPlatform the Embabel platform
* @param agent the deployed {@link Agent} for this aggregate
* @param agentProducedErrors the error types the agent may produce
* @param runtimeBuilder the runtime builder to register with
*/
@SuppressWarnings({"unchecked", "rawtypes"})
private void processAgentHandledCommand(
Class<? extends Command> commandClass,
AgenticAggregate<S> aggregate,
String aggregateName,
AgentPlatform agentPlatform,
Agent agent,
List<DomainEventType<?>> agentProducedErrors,
KafkaAggregateRuntime.Builder runtimeBuilder) {

Expand All @@ -386,9 +355,9 @@ private void processAgentHandledCommand(

AgenticCommandHandlerFunctionAdapter adapter = new AgenticCommandHandlerFunctionAdapter<>(
(AgenticAggregate) aggregate,
aggregateName,
commandType,
agentPlatform,
agent,
(List) List.of(), // producedDomainEventTypes: empty — events are registered via EventSourcingHandler adapters
(List) errorTypes,
// TODO: wire aggregateServicesSupplier from AkcesAgenticAggregateController (Phase 3)
Expand All @@ -408,17 +377,17 @@ private void processAgentHandledCommand(
*
* @param eventClass the external domain event class to register
* @param aggregate the owning aggregate instance
* @param aggregateName the aggregate name used for agent inference at runtime
* @param agentPlatform the Embabel platform
* @param agent the deployed {@link Agent} for this aggregate
* @param agentProducedErrors the error types the agent may produce
* @param runtimeBuilder the runtime builder to register with
*/
@SuppressWarnings({"unchecked", "rawtypes"})
private void processAgentHandledEvent(
Class<? extends DomainEvent> eventClass,
AgenticAggregate<S> aggregate,
String aggregateName,
AgentPlatform agentPlatform,
Agent agent,
List<DomainEventType<?>> agentProducedErrors,
KafkaAggregateRuntime.Builder runtimeBuilder) {

Expand Down Expand Up @@ -449,9 +418,9 @@ private void processAgentHandledEvent(

AgenticEventHandlerFunctionAdapter adapter = new AgenticEventHandlerFunctionAdapter<>(
(AgenticAggregate) aggregate,
aggregateName,
eventType,
agentPlatform,
agent,
(List) List.of(), // producedDomainEventTypes: empty — events are registered via EventSourcingHandler adapters
(List) errorTypes,
// TODO: wire aggregateServicesSupplier from AkcesAgenticAggregateController (Phase 3)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2022 - 2026 The Original Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.elasticsoftware.akces.agentic.embabel;

import com.embabel.agent.api.annotation.Agent;
import com.embabel.agent.api.common.PlannerType;

/**
* The default Embabel agent that is always present on the {@link com.embabel.agent.core.AgentPlatform}.
* This agent is used as a fallback when no other agent matches the aggregate name during
* agent resolution in the agentic command and event handler adapters.
*
* <p>Uses the {@link PlannerType#UTILITY UTILITY} planner for flexible goal-based planning.
* Actions and conditions will be added as the agentic framework evolves.
*/
@Agent(description = "Default Akces agentic aggregate agent", planner = PlannerType.UTILITY)
public class DefaultAgent {
}
Loading
Loading