Skip to content

Commit 1ce5c1c

Browse files
committed
workflow observation
1 parent 9a2becd commit 1ce5c1c

File tree

17 files changed

+316
-11
lines changed

17 files changed

+316
-11
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
# Agentic Patterns
1+
# Agentic Workflow and Patterns
22

33
[![build](https://github.com/JavaAIDev/agentic-patterns/actions/workflows/build.yaml/badge.svg)](https://github.com/JavaAIDev/agentic-patterns/actions/workflows/build.yaml)
44
![Maven Central Version](https://img.shields.io/maven-central/v/com.javaaidev.agenticpatterns/agentic-patterns)
55

66
Reference implementation of [Agentic Patterns](https://javaaidev.com/docs/agentic-patterns/intro/)
77

8-
This reference implementation is designed to be reusable.
9-
108
Requirements:
119

1210
- Java 21 (for virtual threads)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.javaaidev.agenticpatterns.core;
2+
3+
import com.javaaidev.agenticpatterns.core.observation.DefaultWorkflowExecutionObservationConvention;
4+
import com.javaaidev.agenticpatterns.core.observation.WorkflowExecutionObservationContext;
5+
import com.javaaidev.agenticpatterns.core.observation.WorkflowExecutionObservationDocumentation;
6+
import io.micrometer.observation.ObservationRegistry;
7+
import org.jspecify.annotations.Nullable;
8+
9+
public abstract class AbstractAgenticWorkflow<Request, Response> implements
10+
AgenticWorkflow<Request, Response> {
11+
12+
@Nullable
13+
protected String workflowName;
14+
@Nullable
15+
protected ObservationRegistry observationRegistry;
16+
17+
protected AbstractAgenticWorkflow(@Nullable String workflowName,
18+
@Nullable ObservationRegistry observationRegistry) {
19+
this.workflowName = workflowName;
20+
this.observationRegistry = observationRegistry;
21+
}
22+
23+
@Override
24+
public String getName() {
25+
return this.workflowName != null ? this.workflowName : this.getClass().getSimpleName();
26+
}
27+
28+
@Override
29+
public Response execute(@Nullable Request request) {
30+
if (observationRegistry == null || observationRegistry.isNoop()) {
31+
return doExecute(request);
32+
}
33+
var observationContext = new WorkflowExecutionObservationContext(getName(), request);
34+
var observation =
35+
WorkflowExecutionObservationDocumentation.WORKFLOW_EXECUTION.observation(
36+
null,
37+
new DefaultWorkflowExecutionObservationConvention(),
38+
() -> observationContext,
39+
observationRegistry
40+
).start();
41+
try (var ignored = observation.openScope()) {
42+
var response = doExecute(request);
43+
observationContext.setResponse(response);
44+
return response;
45+
} catch (Exception e) {
46+
observation.error(e);
47+
throw new WorkflowExecutionException("Error in workflow execution", e);
48+
} finally {
49+
observation.stop();
50+
}
51+
}
52+
53+
protected abstract Response doExecute(@Nullable Request request);
54+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.javaaidev.agenticpatterns.core;
2+
3+
import org.jspecify.annotations.Nullable;
4+
5+
public interface AgenticWorkflow<Request, Response> {
6+
7+
Response execute(@Nullable Request request);
8+
9+
default String getName() {
10+
return this.getClass().getSimpleName();
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.javaaidev.agenticpatterns.core;
2+
3+
public class WorkflowExecutionException extends RuntimeException {
4+
5+
public WorkflowExecutionException(String message) {
6+
super(message);
7+
}
8+
9+
public WorkflowExecutionException(String message, Throwable cause) {
10+
super(message, cause);
11+
}
12+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.javaaidev.agenticpatterns.core.observation;
2+
3+
import com.javaaidev.agenticpatterns.core.AgentUtils;
4+
import com.javaaidev.agenticpatterns.core.observation.WorkflowExecutionObservationDocumentation.HighCardinalityKeyNames;
5+
import com.javaaidev.agenticpatterns.core.observation.WorkflowExecutionObservationDocumentation.LowCardinalityKeyNames;
6+
import io.micrometer.common.KeyValue;
7+
import io.micrometer.common.KeyValues;
8+
9+
public class DefaultWorkflowExecutionObservationConvention implements
10+
WorkflowExecutionObservationConvention {
11+
12+
private String defaultName = "workflow.execute";
13+
14+
@Override
15+
public String getName() {
16+
return defaultName;
17+
}
18+
19+
@Override
20+
public KeyValues getLowCardinalityKeyValues(WorkflowExecutionObservationContext context) {
21+
return KeyValues.of(workflowName(context));
22+
}
23+
24+
@Override
25+
public KeyValues getHighCardinalityKeyValues(WorkflowExecutionObservationContext context) {
26+
return KeyValues.of(
27+
workflowExecutionInput(context),
28+
workflowExecutionOutput(context)
29+
);
30+
}
31+
32+
private KeyValue workflowName(WorkflowExecutionObservationContext context) {
33+
return KeyValue.of(
34+
LowCardinalityKeyNames.WORKFLOW_NAME, context.getWorkflowName()
35+
);
36+
}
37+
38+
private KeyValue workflowExecutionInput(WorkflowExecutionObservationContext context) {
39+
return KeyValue.of(
40+
HighCardinalityKeyNames.WORKFLOW_EXECUTION_INPUT,
41+
context.getCarrier() != null ? AgentUtils.toJson(context.getCarrier()) : KeyValue.NONE_VALUE
42+
);
43+
}
44+
45+
private KeyValue workflowExecutionOutput(WorkflowExecutionObservationContext context) {
46+
return KeyValue.of(
47+
HighCardinalityKeyNames.WORKFLOW_EXECUTION_OUTPUT,
48+
context.getResponse() != null ? AgentUtils.toJson(context.getResponse())
49+
: KeyValue.NONE_VALUE
50+
);
51+
}
52+
53+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.javaaidev.agenticpatterns.core.observation;
2+
3+
import io.micrometer.observation.transport.RequestReplySenderContext;
4+
5+
public class WorkflowExecutionObservationContext extends RequestReplySenderContext<Object, Object> {
6+
7+
private final String workflowName;
8+
9+
public WorkflowExecutionObservationContext(
10+
String workflowName, Object input) {
11+
super((carrier, key, value) -> {
12+
13+
});
14+
this.workflowName = workflowName;
15+
setCarrier(input);
16+
}
17+
18+
public String getWorkflowName() {
19+
return workflowName;
20+
}
21+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.javaaidev.agenticpatterns.core.observation;
2+
3+
import io.micrometer.observation.Observation.Context;
4+
import io.micrometer.observation.ObservationConvention;
5+
6+
public interface WorkflowExecutionObservationConvention extends
7+
ObservationConvention<WorkflowExecutionObservationContext> {
8+
9+
@Override
10+
default boolean supportsContext(Context context) {
11+
return context instanceof WorkflowExecutionObservationContext;
12+
}
13+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.javaaidev.agenticpatterns.core.observation;
2+
3+
import io.micrometer.common.docs.KeyName;
4+
import io.micrometer.observation.Observation.Context;
5+
import io.micrometer.observation.ObservationConvention;
6+
import io.micrometer.observation.docs.ObservationDocumentation;
7+
8+
public enum WorkflowExecutionObservationDocumentation implements ObservationDocumentation {
9+
WORKFLOW_EXECUTION {
10+
@Override
11+
public Class<? extends ObservationConvention<? extends Context>> getDefaultConvention() {
12+
return DefaultWorkflowExecutionObservationConvention.class;
13+
}
14+
15+
@Override
16+
public KeyName[] getLowCardinalityKeyNames() {
17+
return LowCardinalityKeyNames.values();
18+
}
19+
20+
@Override
21+
public KeyName[] getHighCardinalityKeyNames() {
22+
return HighCardinalityKeyNames.values();
23+
}
24+
};
25+
26+
public enum LowCardinalityKeyNames implements KeyName {
27+
WORKFLOW_NAME {
28+
@Override
29+
public String asString() {
30+
return "workflow.name";
31+
}
32+
}
33+
}
34+
35+
public enum HighCardinalityKeyNames implements KeyName {
36+
WORKFLOW_EXECUTION_INPUT {
37+
@Override
38+
public String asString() {
39+
return "workflow.execution.input";
40+
}
41+
},
42+
43+
WORKFLOW_EXECUTION_OUTPUT {
44+
@Override
45+
public String asString() {
46+
return "workflow.execution.output";
47+
}
48+
}
49+
}
50+
}

evaluator-optimizer/src/main/java/com/javaaidev/agenticpatterns/evaluatoroptimizer/BooleanEvaluationResult.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package com.javaaidev.agenticpatterns.evaluatoroptimizer;
22

3+
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
34
import org.jspecify.annotations.Nullable;
45

56
/**
6-
* Evaluation result
7+
* Boolean evaluation result
78
*
89
* @param passed Passed or not passed
910
* @param feedback Feedback if not passed
1011
*/
11-
public record BooleanEvaluationResult(boolean passed, @Nullable String feedback) implements
12+
public record BooleanEvaluationResult(
13+
@JsonPropertyDescription("If evaluation passed") boolean passed,
14+
@JsonPropertyDescription("Feedback of evaluation") @Nullable String feedback) implements
1215
EvaluationResult {
1316

1417
@Override

evaluator-optimizer/src/main/java/com/javaaidev/agenticpatterns/evaluatoroptimizer/EvaluationResult.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,16 @@
22

33
import org.jspecify.annotations.Nullable;
44

5+
/**
6+
* Evaluation result
7+
*/
58
public interface EvaluationResult {
69

10+
/**
11+
* Feedback of evaluation
12+
*
13+
* @return feedback
14+
*/
715
@Nullable
816
String getFeedback();
917
}

0 commit comments

Comments
 (0)