Skip to content

Commit 091668c

Browse files
authored
LangChain4j integration update (helidon-io#10362)
LangChain4j integration update * Code-generated lc4j integrations * LangChain4j OCI GenAi provider * Jlama provider * Pluggable HttpClient * Pluggable ChatModelListeners * TokenStreamAdapter for Java Stream Signed-off-by: Daniel Kec <daniel.kec@oracle.com>
1 parent cf80bd6 commit 091668c

129 files changed

Lines changed: 5436 additions & 3278 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

all/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,14 @@
835835
<groupId>io.helidon.integrations.langchain4j.providers</groupId>
836836
<artifactId>helidon-integrations-langchain4j-providers-oracle</artifactId>
837837
</dependency>
838+
<dependency>
839+
<groupId>io.helidon.integrations.langchain4j.providers</groupId>
840+
<artifactId>helidon-integrations-langchain4j-providers-oci-genai</artifactId>
841+
</dependency>
842+
<dependency>
843+
<groupId>io.helidon.integrations.langchain4j.providers</groupId>
844+
<artifactId>helidon-integrations-langchain4j-providers-jlama</artifactId>
845+
</dependency>
838846
<dependency>
839847
<groupId>io.helidon.openapi</groupId>
840848
<artifactId>helidon-openapi</artifactId>

bom/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,16 @@
11141114
<artifactId>helidon-integrations-langchain4j-providers-oracle</artifactId>
11151115
<version>${helidon.version}</version>
11161116
</dependency>
1117+
<dependency>
1118+
<groupId>io.helidon.integrations.langchain4j.providers</groupId>
1119+
<artifactId>helidon-integrations-langchain4j-providers-oci-genai</artifactId>
1120+
<version>${helidon.version}</version>
1121+
</dependency>
1122+
<dependency>
1123+
<groupId>io.helidon.integrations.langchain4j.providers</groupId>
1124+
<artifactId>helidon-integrations-langchain4j-providers-jlama</artifactId>
1125+
<version>${helidon.version}</version>
1126+
</dependency>
11171127

11181128
<!-- OpenAPI support -->
11191129
<dependency>

builder/codegen/src/main/java/io/helidon/builder/codegen/FactoryMethods.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import static io.helidon.builder.codegen.Types.PROTOTYPE_FACTORY_METHOD;
3434
import static io.helidon.builder.codegen.Types.RUNTIME_API;
3535
import static io.helidon.codegen.CodegenUtil.capitalize;
36+
import static io.helidon.codegen.ElementInfoPredicates.elementName;
3637
import static io.helidon.common.types.TypeNames.OBJECT;
3738

3839
/*
@@ -118,9 +119,17 @@ private static Optional<FactoryMethod> builder(CodegenContext ctx,
118119
.stream()
119120
.filter(ElementInfoPredicates::isMethod)
120121
.filter(ElementInfoPredicates::isStatic)
121-
.filter(ElementInfoPredicates.elementName("builder"))
122+
.filter(elementName("builder"))
122123
.filter(ElementInfoPredicates::hasNoArgs)
123124
.filter(it -> it.typeName().className().equals("Builder"))
125+
// Has to have public no-param build method returning right type
126+
.filter(it -> ctx.typeInfo(it.typeName()).stream()
127+
.flatMap(builderTypeInfo -> builderTypeInfo.elementInfo().stream())
128+
.filter(ElementInfoPredicates::isMethod)
129+
.filter(ElementInfoPredicates::isPublic)
130+
.filter(ElementInfoPredicates::hasNoArgs)
131+
.filter(m -> m.typeName().equals(typeInfo.typeName()))
132+
.anyMatch(elementName("build")))
124133
.findFirst()
125134
.map(it -> new FactoryMethod(builderCandidate,
126135
copyGenericTypes(builderCandidate, it.typeName()),
@@ -367,7 +376,7 @@ private static Optional<TypeName> findFactoryMethodByParamType(TypeInfo declarin
367376
// @FactoryMethod
368377
.filter(ElementInfoPredicates.hasAnnotation(PROTOTYPE_FACTORY_METHOD))
369378
// createMyProperty
370-
.filter(ElementInfoPredicates.elementName(methodName))
379+
.filter(elementName(methodName))
371380
// must have a single parameter of the correct type
372381
.filter(ElementInfoPredicates.hasParams(paramType))
373382
.map(TypedElementInfo::typeName)

builder/codegen/src/main/java/io/helidon/builder/codegen/GenerateAbstractBuilder.java

Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import static io.helidon.builder.codegen.Types.BUILDER_SUPPORT;
4646
import static io.helidon.builder.codegen.Types.CONFIG_BUILDER_SUPPORT;
4747
import static io.helidon.builder.codegen.Types.REGISTRY_BUILDER_SUPPORT;
48+
import static io.helidon.builder.codegen.Types.SERVICE_NAMED;
4849
import static io.helidon.codegen.CodegenUtil.capitalize;
4950
import static io.helidon.common.types.TypeNames.LIST;
5051
import static io.helidon.common.types.TypeNames.MAP;
@@ -53,6 +54,8 @@
5354

5455
final class GenerateAbstractBuilder {
5556

57+
private static final String SERVICE_REGISTRY_CONFIG_KEY = "service-registry";
58+
5659
private GenerateAbstractBuilder() {
5760
}
5861

@@ -385,10 +388,21 @@ public BUILDER config(Config config) {
385388
if (configured.configured()) {
386389
for (PrototypeProperty child : properties) {
387390
if (child.configuredOption().configured() && !child.configuredOption().provider()) {
388-
// registry service can never reach here, as they do not support Option.Configured
391+
if (child.registryService()) {
392+
// Injectable option can have a qualifier configured instead of an actual value
393+
builder.addContent("if (!config.get(")
394+
.addContentLiteral(child.configuredOption().configKey() + "." + SERVICE_REGISTRY_CONFIG_KEY)
395+
.addContentLine(").exists()) {")
396+
.increaseContentPadding();
397+
}
398+
389399
child.typeHandler().generateFromConfig(builder,
390400
child.configuredOption(),
391401
child.factoryMethods());
402+
if (child.registryService()) {
403+
builder.decreaseContentPadding()
404+
.addContentLine("}");
405+
}
392406
}
393407
}
394408
}
@@ -595,7 +609,7 @@ private static void preBuildPrototypeMethod(InnerClass.Builder classBuilder,
595609
if (typeContext.typeInfo().supportsServiceRegistry() || typeContext.propertyData().hasProvider()) {
596610
boolean configured = typeContext.configuredData().configured();
597611

598-
if (configured && typeContext.propertyData().hasProvider()) {
612+
if (configured) {
599613
// need to have a non-null config instance
600614
preBuildBuilder.addContent("var config = this.config == null ? ")
601615
.addContent(Types.COMMON_CONFIG)
@@ -724,7 +738,59 @@ private static void serviceLoaderPropertyDiscovery(Method.Builder preBuildBuilde
724738
private static void serviceRegistryProperty(Method.Builder preBuildBuilder,
725739
PrototypeProperty property) {
726740
TypeName typeName = property.typeHandler().declaredType();
741+
742+
// Example: .of("bean-name") or .empty()
743+
var namedQualifierFromAnnotation = property.qualifiers()
744+
.stream()
745+
.filter(a -> a.typeName().equals(SERVICE_NAMED))
746+
.flatMap(annotation -> annotation.stringValue().stream())
747+
.map(s -> ".of(\"" + s + "\")")
748+
.findFirst()
749+
.orElse(".empty()");
750+
751+
// Example: Optional<String> regionQualifier =
752+
preBuildBuilder
753+
.addContent("Optional<String> ")
754+
.addContent(property.name())
755+
.addContent("Qualifier = ");
756+
757+
if (property.configuredOption().configured()) {
758+
/* Configured named qualifier wins over annotation
759+
Example:
760+
Optional<String> regionQualifier = config.get("region.service-registry.named")
761+
.asString()
762+
.orElse(Optional.of("bean-name"));
763+
*/
764+
preBuildBuilder
765+
.addContent("config.get(")
766+
.addContentLiteral(property.configuredOption().configKey() + "." + SERVICE_REGISTRY_CONFIG_KEY + ".named")
767+
.addContentLine(")")
768+
.increaseContentPadding()
769+
.addContentLine(".asString()")
770+
.addContent(".or(() -> ")
771+
.addContent(OPTIONAL)
772+
.addContent(namedQualifierFromAnnotation)
773+
.addContentLine(");")
774+
.decreaseContentPadding();
775+
} else {
776+
/* Example:
777+
Optional<String> regionQualifier = Optional.of("bean-name");
778+
*/
779+
preBuildBuilder
780+
.addContent(OPTIONAL)
781+
.addContent(namedQualifierFromAnnotation)
782+
.addContentLine(";");
783+
}
784+
727785
if (typeName.isList()) {
786+
787+
/*
788+
this.addRegion(RegistryBuilderSupport.serviceList(registry,
789+
TypeName.create("com.oracle.bmc.Region"),
790+
regionQualifier,
791+
Optional.ofNullable(region),
792+
regionDiscoverServices));
793+
*/
728794
preBuildBuilder
729795
.addContent("this.add")
730796
.addContent(capitalize(property.name()))
@@ -734,8 +800,18 @@ private static void serviceRegistryProperty(Method.Builder preBuildBuilder,
734800
.addContentCreate(property.typeHandler().actualType())
735801
.addContent(", ")
736802
.addContent(property.name())
737-
.addContentLine("DiscoverServices));");
803+
.addContent("DiscoverServices, ")
804+
.addContent(property.name())
805+
.addContentLine("Qualifier));");
806+
738807
} else if (typeName.isSet()) {
808+
/*
809+
this.addRegion(RegistryBuilderSupport.serviceSet(registry,
810+
TypeName.create("com.oracle.bmc.Region"),
811+
regionQualifier,
812+
Optional.ofNullable(region),
813+
regionDiscoverServices));
814+
*/
739815
preBuildBuilder
740816
.addContent("this.add")
741817
.addContent(capitalize(property.name()))
@@ -745,8 +821,19 @@ private static void serviceRegistryProperty(Method.Builder preBuildBuilder,
745821
.addContentCreate(property.typeHandler().actualType())
746822
.addContent(", ")
747823
.addContent(property.name())
748-
.addContentLine("DiscoverServices));");
824+
.addContent("DiscoverServices, ")
825+
.addContent(property.name())
826+
.addContentLine("Qualifier));");
749827
} else {
828+
829+
/*
830+
RegistryBuilderSupport.service(registry,
831+
TypeName.create("com.oracle.bmc.Region"),
832+
regionQualifiers,
833+
Optional.ofNullable(region),
834+
regionDiscoverServices)
835+
.ifPresent(this::region);
836+
*/
750837
preBuildBuilder
751838
.addContent(REGISTRY_BUILDER_SUPPORT)
752839
.addContent(".service(registry, ")
@@ -757,9 +844,14 @@ private static void serviceRegistryProperty(Method.Builder preBuildBuilder,
757844
.addContent(property.name())
758845
.addContent("), ")
759846
.addContent(property.name())
760-
.addContent("DiscoverServices).ifPresent(this::")
847+
.addContent("DiscoverServices, ")
848+
.addContent(property.name())
849+
.addContentLine("Qualifier)")
850+
.increaseContentPadding()
851+
.addContent(".ifPresent(this::")
761852
.addContent(property.setterName())
762-
.addContentLine(");");
853+
.addContentLine(");")
854+
.decreaseContentPadding();
763855
}
764856
}
765857

builder/codegen/src/main/java/io/helidon/builder/codegen/PrototypeProperty.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package io.helidon.builder.codegen;
1818

19+
import java.util.List;
1920
import java.util.Optional;
2021
import java.util.Set;
2122

@@ -40,7 +41,8 @@ record PrototypeProperty(MethodSignature signature,
4041
boolean equality, // part of equals and hash code
4142
boolean toStringValue, // part of toString
4243
boolean confidential, // if part of toString, do not print the actual value,
43-
boolean registryService
44+
boolean registryService,
45+
List<Annotation> qualifiers
4446
) {
4547
// cannot be identifiers - such as field name or method name
4648
private static final Set<String> RESERVED_WORDS = Set.of(
@@ -103,6 +105,10 @@ static PrototypeProperty create(CodegenContext ctx,
103105
.map(Boolean::parseBoolean)
104106
.orElse(false);
105107
boolean registryService = element.hasAnnotation(Types.OPTION_REGISTRY_SERVICE);
108+
var qualifiers = element.annotations()
109+
.stream()
110+
.filter(a -> a.hasMetaAnnotation(Types.SERVICE_QUALIFIER))
111+
.toList();
106112

107113
return new PrototypeProperty(
108114
MethodSignature.create(element),
@@ -112,7 +118,8 @@ static PrototypeProperty create(CodegenContext ctx,
112118
equality,
113119
toStringValue,
114120
confidential,
115-
registryService
121+
registryService,
122+
qualifiers
116123
);
117124
}
118125

builder/codegen/src/main/java/io/helidon/builder/codegen/TypeContext.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
21
/*
3-
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2025 Oracle and/or its affiliates.
43
*
54
* Licensed under the Apache License, Version 2.0 (the "License");
65
* you may not use this file except in compliance with the License.
@@ -18,12 +17,14 @@
1817
package io.helidon.builder.codegen;
1918

2019
import java.util.ArrayList;
20+
import java.util.Collection;
2121
import java.util.HashSet;
2222
import java.util.LinkedHashSet;
2323
import java.util.List;
2424
import java.util.Optional;
2525
import java.util.Set;
2626
import java.util.function.Predicate;
27+
import java.util.stream.Stream;
2728

2829
import io.helidon.codegen.CodegenContext;
2930
import io.helidon.codegen.ElementInfoPredicates;
@@ -214,10 +215,11 @@ static TypeContext create(CodegenContext ctx, TypeInfo blueprint) {
214215
.stream()
215216
.filter(ElementInfoPredicates::isMethod)
216217
.anyMatch(ElementInfoPredicates.hasAnnotation(Types.OPTION_PROVIDER));
217-
supportsServiceRegistry |= blueprint.elementInfo()
218-
.stream()
219-
.filter(ElementInfoPredicates::isMethod)
220-
.anyMatch(ElementInfoPredicates.hasAnnotation(Types.OPTION_REGISTRY_SERVICE));
218+
supportsServiceRegistry |= Stream.concat(Stream.of(blueprint), blueprint.interfaceTypeInfo().stream())
219+
.map(TypeInfo::elementInfo)
220+
.flatMap(Collection::stream)
221+
.filter(ElementInfoPredicates::isMethod)
222+
.anyMatch(ElementInfoPredicates.hasAnnotation(Types.OPTION_REGISTRY_SERVICE));
221223

222224
TypeInformation typeInformation = new TypeInformation(supportsServiceRegistry,
223225
blueprint,

builder/codegen/src/main/java/io/helidon/builder/codegen/TypeHandlerOptional.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -184,9 +184,12 @@ void setters(InnerClass.Builder classBuilder,
184184
.addContent(fm.typeWithFactoryMethod().genericTypeName())
185185
.addContent(".")
186186
.update(it -> {
187-
if (!finalBuilderType.typeArguments().isEmpty()) {
187+
Iterator<TypeName> iterator = finalBuilderType.typeArguments()
188+
.stream()
189+
.filter(t -> !t.name().equals("?"))
190+
.iterator();
191+
if (iterator.hasNext()) {
188192
it.addContent("<");
189-
Iterator<TypeName> iterator = finalBuilderType.typeArguments().iterator();
190193
while (iterator.hasNext()) {
191194
it.addContent(iterator.next());
192195
if (iterator.hasNext()) {

builder/codegen/src/main/java/io/helidon/builder/codegen/Types.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ final class Types {
3737
static final TypeName SERVICE_REGISTRY = TypeName.create("io.helidon.service.registry.ServiceRegistry");
3838
static final TypeName GLOBAL_SERVICE_REGISTRY = TypeName.create("io.helidon.service.registry.GlobalServiceRegistry");
3939
static final TypeName SERVICES = TypeName.create("io.helidon.service.registry.Services");
40+
static final TypeName SERVICE_NAMED = TypeName.create("io.helidon.service.registry.Service.Named");
41+
static final TypeName SERVICE_QUALIFIER = TypeName.create("io.helidon.service.registry.Service.Qualifier");
4042

4143
static final TypeName BUILDER_DESCRIPTION = TypeName.create("io.helidon.builder.api.Description");
4244

builder/tests/codegen/src/test/java/io/helidon/builder/codegen/TypesTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import io.helidon.common.types.TypeName;
4141
import io.helidon.service.registry.GlobalServiceRegistry;
4242
import io.helidon.service.registry.RegistryBuilderSupport;
43+
import io.helidon.service.registry.Service;
4344
import io.helidon.service.registry.ServiceRegistry;
4445
import io.helidon.service.registry.Services;
4546

@@ -137,6 +138,8 @@ void testTypes() {
137138
checkField(toCheck, checked, fields, "BUILDER_SUPPORT", BuilderSupport.class);
138139

139140
checkField(toCheck, checked, fields, "SERVICES", Services.class);
141+
checkField(toCheck, checked, fields, "SERVICE_NAMED", Service.Named.class);
142+
checkField(toCheck, checked, fields, "SERVICE_QUALIFIER", Service.Qualifier.class);
140143

141144
checkField(toCheck, checked, fields, "CONFIG_BUILDER_SUPPORT", ConfigBuilderSupport.class);
142145
checkField(toCheck, checked, fields, "CONFIG_CONFIGURED_BUILDER", ConfigBuilderSupport.ConfiguredBuilder.class);

codegen/class-model/src/main/java/io/helidon/codegen/classmodel/AnnotatedComponent.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -62,7 +62,7 @@ public B description(List<String> description) {
6262

6363
@Override
6464
public B addDescriptionLine(String line) {
65-
return super.description(line);
65+
return super.addDescriptionLine(line);
6666
}
6767

6868
/**

0 commit comments

Comments
 (0)