Skip to content

Commit 2103f07

Browse files
authored
Merge pull request #1097 from jmartisk/lc4j-0.36.2
Upgrade to LangChain4j 0.36.2
2 parents 0f77ff2 + 3168cce commit 2103f07

File tree

16 files changed

+129
-19
lines changed

16 files changed

+129
-19
lines changed

core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/BeansProcessor.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import io.quarkus.deployment.builditem.FeatureBuildItem;
5757
import io.quarkus.deployment.builditem.IndexDependencyBuildItem;
5858
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
59+
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
5960
import io.quarkus.deployment.logging.LogCleanupFilterBuildItem;
6061
import io.quarkus.runtime.configuration.ConfigurationException;
6162

@@ -608,4 +609,10 @@ void logCleanupFilters(BuildProducer<LogCleanupFilterBuildItem> logCleanupFilter
608609
logCleanupFilters
609610
.produce(new LogCleanupFilterBuildItem("ai.djl.huggingface.tokenizers.jni.LibUtils", Level.INFO, "Extracting"));
610611
}
612+
613+
@BuildStep
614+
public void nativeSupport(BuildProducer<RuntimeInitializedClassBuildItem> producer) {
615+
// RetryUtils initializes a java.lang.Random instance
616+
producer.produce(new RuntimeInitializedClassBuildItem("dev.langchain4j.internal.RetryUtils"));
617+
}
611618
}

core/runtime/src/main/java/io/quarkiverse/langchain4j/QuarkusJsonCodecFactory.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.io.IOException;
55
import java.io.InputStream;
66
import java.io.UncheckedIOException;
7+
import java.lang.reflect.Type;
78
import java.util.Map;
89
import java.util.regex.Matcher;
910
import java.util.regex.Pattern;
@@ -15,6 +16,7 @@
1516
import com.fasterxml.jackson.core.JsonProcessingException;
1617
import com.fasterxml.jackson.core.json.JsonReadFeature;
1718
import com.fasterxml.jackson.core.type.TypeReference;
19+
import com.fasterxml.jackson.databind.JavaType;
1820
import com.fasterxml.jackson.databind.ObjectMapper;
1921
import com.fasterxml.jackson.databind.ObjectWriter;
2022
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
@@ -60,6 +62,23 @@ public <T> T fromJson(String json, Class<T> type) {
6062
}
6163
}
6264

65+
@Override
66+
public <T> T fromJson(String json, Type type) {
67+
JavaType javaType = ObjectMapperHolder.MAPPER.constructType(type);
68+
try {
69+
String sanitizedJson = sanitize(json, javaType.getRawClass());
70+
return ObjectMapperHolder.MAPPER.readValue(sanitizedJson, javaType);
71+
} catch (JsonProcessingException e) {
72+
if ((e instanceof JsonParseException) && (javaType.isEnumType())) {
73+
// this is the case where LangChain4j simply passes the string value of the enum to Json.fromJson()
74+
// and Jackson does not handle it
75+
Class<? extends Enum> enumClass = javaType.getRawClass().asSubclass(Enum.class);
76+
return (T) Enum.valueOf(enumClass, json);
77+
}
78+
throw new UncheckedIOException(e);
79+
}
80+
}
81+
6382
private <T> String sanitize(String original, Class<T> type) {
6483
if (String.class.equals(type)) {
6584
return original;

core/runtime/src/main/java/io/quarkiverse/langchain4j/runtime/aiservice/AiServiceMethodCreateInfo.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package io.quarkiverse.langchain4j.runtime.aiservice;
22

3-
import static org.apache.commons.lang3.StringUtils.EMPTY;
4-
53
import java.lang.reflect.Type;
64
import java.util.List;
75
import java.util.Map;
@@ -202,7 +200,7 @@ public String getUserMessageTemplate() {
202200
Optional<String> userMessageTemplateOpt = this.getUserMessageInfo().template()
203201
.flatMap(AiServiceMethodCreateInfo.TemplateInfo::text);
204202

205-
return userMessageTemplateOpt.orElse(EMPTY);
203+
return userMessageTemplateOpt.orElse("");
206204
}
207205

208206
public boolean isSwitchToWorkerThread() {

core/runtime/src/main/java/io/quarkiverse/langchain4j/runtime/aiservice/QuarkusAiServiceStreamingResponseHandler.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import dev.langchain4j.model.output.Response;
2121
import dev.langchain4j.model.output.TokenUsage;
2222
import dev.langchain4j.service.AiServiceContext;
23+
import dev.langchain4j.service.tool.ToolExecution;
2324
import dev.langchain4j.service.tool.ToolExecutor;
2425
import io.smallrye.mutiny.infrastructure.Infrastructure;
2526
import io.vertx.core.Context;
@@ -38,6 +39,7 @@ public class QuarkusAiServiceStreamingResponseHandler implements StreamingRespon
3839

3940
private final Consumer<String> tokenHandler;
4041
private final Consumer<Response<AiMessage>> completionHandler;
42+
private final Consumer<ToolExecution> toolExecuteHandler;
4143
private final Consumer<Throwable> errorHandler;
4244

4345
private final List<ChatMessage> temporaryMemory;
@@ -51,6 +53,7 @@ public class QuarkusAiServiceStreamingResponseHandler implements StreamingRespon
5153
QuarkusAiServiceStreamingResponseHandler(AiServiceContext context,
5254
Object memoryId,
5355
Consumer<String> tokenHandler,
56+
Consumer<ToolExecution> toolExecuteHandler,
5457
Consumer<Response<AiMessage>> completionHandler,
5558
Consumer<Throwable> errorHandler,
5659
List<ChatMessage> temporaryMemory,
@@ -62,6 +65,7 @@ public class QuarkusAiServiceStreamingResponseHandler implements StreamingRespon
6265

6366
this.tokenHandler = ensureNotNull(tokenHandler, "tokenHandler");
6467
this.completionHandler = completionHandler;
68+
this.toolExecuteHandler = toolExecuteHandler;
6569
this.errorHandler = errorHandler;
6670

6771
this.temporaryMemory = new ArrayList<>(temporaryMemory);
@@ -116,6 +120,12 @@ public void run() {
116120
ToolExecutionResultMessage toolExecutionResultMessage = ToolExecutionResultMessage.from(
117121
toolExecutionRequest,
118122
toolExecutionResult);
123+
ToolExecution toolExecution = ToolExecution.builder()
124+
.request(toolExecutionRequest).result(toolExecutionResult)
125+
.build();
126+
if (toolExecuteHandler != null) {
127+
toolExecuteHandler.accept(toolExecution);
128+
}
119129
QuarkusAiServiceStreamingResponseHandler.this.addToMemory(toolExecutionResultMessage);
120130
}
121131

@@ -126,6 +136,7 @@ public void run() {
126136
context,
127137
memoryId,
128138
tokenHandler,
139+
toolExecuteHandler,
129140
completionHandler,
130141
errorHandler,
131142
temporaryMemory,

core/runtime/src/main/java/io/quarkiverse/langchain4j/runtime/aiservice/QuarkusAiServiceTokenStream.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import dev.langchain4j.rag.content.Content;
2121
import dev.langchain4j.service.AiServiceContext;
2222
import dev.langchain4j.service.TokenStream;
23+
import dev.langchain4j.service.tool.ToolExecution;
2324
import dev.langchain4j.service.tool.ToolExecutor;
2425
import io.vertx.core.Context;
2526

@@ -44,12 +45,14 @@ public class QuarkusAiServiceTokenStream implements TokenStream {
4445
private Consumer<List<Content>> contentsHandler;
4546
private Consumer<Throwable> errorHandler;
4647
private Consumer<Response<AiMessage>> completionHandler;
48+
private Consumer<ToolExecution> toolExecuteHandler;
4749

4850
private int onNextInvoked;
4951
private int onCompleteInvoked;
5052
private int onRetrievedInvoked;
5153
private int onErrorInvoked;
5254
private int ignoreErrorsInvoked;
55+
private int toolExecuteInvoked;
5356

5457
public QuarkusAiServiceTokenStream(List<ChatMessage> messages,
5558
List<ToolSpecification> toolSpecifications,
@@ -82,6 +85,13 @@ public TokenStream onRetrieved(Consumer<List<Content>> contentsHandler) {
8285
return this;
8386
}
8487

88+
@Override
89+
public TokenStream onToolExecuted(Consumer<ToolExecution> toolExecuteHandler) {
90+
this.toolExecuteHandler = toolExecuteHandler;
91+
this.toolExecuteInvoked++;
92+
return this;
93+
}
94+
8595
@Override
8696
public TokenStream onComplete(Consumer<Response<AiMessage>> completionHandler) {
8797
this.completionHandler = completionHandler;
@@ -110,6 +120,7 @@ public void start() {
110120
context,
111121
memoryId,
112122
tokenHandler,
123+
toolExecuteHandler,
113124
completionHandler,
114125
errorHandler,
115126
initTemporaryMemory(context, messages),
@@ -150,6 +161,10 @@ private void validateConfiguration() {
150161
throw new IllegalConfigurationException("onRetrieved must be invoked at most 1 time");
151162
}
152163

164+
if (toolExecuteInvoked > 1) {
165+
throw new IllegalConfigurationException("onToolExecuted must be invoked at most 1 time");
166+
}
167+
153168
if (onErrorInvoked + ignoreErrorsInvoked != 1) {
154169
throw new IllegalConfigurationException("One of onError or ignoreErrors must be invoked exactly 1 time");
155170
}

core/runtime/src/main/java/io/quarkiverse/langchain4j/runtime/tool/ToolSpecificationObjectSubstitution.java

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import dev.langchain4j.agent.tool.ToolParameters;
44
import dev.langchain4j.agent.tool.ToolSpecification;
5+
import dev.langchain4j.model.chat.request.json.JsonObjectSchema;
56
import io.quarkus.runtime.ObjectSubstitution;
67
import io.quarkus.runtime.annotations.RecordableConstructor;
78

@@ -10,26 +11,36 @@ public class ToolSpecificationObjectSubstitution
1011

1112
@Override
1213
public Serialized serialize(ToolSpecification obj) {
13-
return new Serialized(obj.name(), obj.description(), obj.parameters());
14+
return new Serialized(obj.name(), obj.description(), obj.toolParameters(), obj.parameters());
1415
}
1516

1617
@Override
1718
public ToolSpecification deserialize(Serialized obj) {
18-
return ToolSpecification.builder()
19+
ToolSpecification.Builder builder = ToolSpecification.builder()
1920
.name(obj.name)
20-
.description(obj.description)
21-
.parameters(obj.parameters).build();
21+
.description(obj.description);
22+
if (obj.toolParameters != null) {
23+
builder.parameters(obj.toolParameters);
24+
}
25+
if (obj.parameters != null) {
26+
builder.parameters(obj.parameters);
27+
}
28+
return builder.build();
2229
}
2330

2431
public static class Serialized {
2532
private final String name;
2633
private final String description;
27-
private final ToolParameters parameters;
34+
private final ToolParameters toolParameters;
35+
private final JsonObjectSchema parameters;
2836

2937
@RecordableConstructor
30-
public Serialized(String name, String description, ToolParameters parameters) {
38+
public Serialized(String name, String description,
39+
ToolParameters toolParameters,
40+
JsonObjectSchema parameters) {
3141
this.name = name;
3242
this.description = description;
43+
this.toolParameters = toolParameters;
3344
this.parameters = parameters;
3445
}
3546

@@ -41,7 +52,11 @@ public String getDescription() {
4152
return description;
4253
}
4354

44-
public ToolParameters getParameters() {
55+
public ToolParameters getToolParameters() {
56+
return toolParameters;
57+
}
58+
59+
public JsonObjectSchema getParameters() {
4560
return parameters;
4661
}
4762

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
:project-version: 0.21.0
2-
:langchain4j-version: 0.35.0
2+
:langchain4j-version: 0.36.2
33
:examples-dir: ./../examples/

model-providers/anthropic/deployment/src/test/java/io/quarkiverse/langchain4j/anthropic/deployment/AnthropicChatLanguageModelSmokeTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ void blocking() {
9292
"text" : "Hello, how are you today?"
9393
} ]
9494
} ],
95+
"system" : [ ],
9596
"max_tokens" : 1024,
9697
"stream" : false,
9798
"top_k" : 40

model-providers/anthropic/deployment/src/test/java/io/quarkiverse/langchain4j/anthropic/deployment/AnthropicStreamingChatLanguageModelSmokeTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ public void onComplete(Response<AiMessage> response) {
237237
"text" : "Hello, how are you today?"
238238
} ]
239239
} ],
240+
"system" : [ ],
240241
"max_tokens" : 1024,
241242
"stream" : true,
242243
"top_k" : 40

model-providers/jlama/runtime/src/main/java/io/quarkiverse/langchain4j/jlama/JlamaModel.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import com.github.tjake.jlama.safetensors.prompt.Tool;
1515

1616
import dev.langchain4j.agent.tool.ToolSpecification;
17+
import dev.langchain4j.model.chat.request.json.JsonSchemaElement;
18+
import dev.langchain4j.model.chat.request.json.JsonSchemaElementHelper;
1719
import dev.langchain4j.model.output.FinishReason;
1820

1921
/**
@@ -125,8 +127,16 @@ static Tool toTool(ToolSpecification toolSpecification) {
125127
.name(toolSpecification.name())
126128
.description(toolSpecification.description());
127129

128-
for (Map.Entry<String, Map<String, Object>> p : toolSpecification.parameters().properties().entrySet()) {
129-
builder.addParameter(p.getKey(), p.getValue(), toolSpecification.parameters().required().contains(p.getKey()));
130+
if (toolSpecification.toolParameters() != null) {
131+
for (Map.Entry<String, Map<String, Object>> p : toolSpecification.toolParameters().properties().entrySet()) {
132+
builder.addParameter(p.getKey(), p.getValue(),
133+
toolSpecification.toolParameters().required().contains(p.getKey()));
134+
}
135+
} else if (toolSpecification.parameters() != null) {
136+
for (Map.Entry<String, JsonSchemaElement> p : toolSpecification.parameters().properties().entrySet()) {
137+
builder.addParameter(p.getKey(), JsonSchemaElementHelper.toMap(p.getValue()),
138+
toolSpecification.parameters().required().contains(p.getKey()));
139+
}
130140
}
131141

132142
return Tool.from(builder.build());

0 commit comments

Comments
 (0)