Skip to content

Commit

Permalink
Merge pull request #11335 from anunnakian/langchain4j_sample
Browse files Browse the repository at this point in the history
Add Spring Boot LangChain4j sample
  • Loading branch information
pascalgrimaud authored Nov 11, 2024
2 parents 296f3bf + d2dce9d commit 28abff4
Show file tree
Hide file tree
Showing 13 changed files with 182 additions and 4 deletions.
1 change: 1 addition & 0 deletions .github/workflows/github-actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ jobs:
- customjhlite
- typescriptapp
- thymeleafapp
- langchain4japp
include:
- java-build-tool: maven
- spring-config-format: yaml
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ from the main (upstream) repository:
- reactiveapp
- customjhlite
- typescriptapp
- langchain4japp
- Below is the list of build tools that can be used for testing (supported input params for the generate.sh script):
- gradle
- maven
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import tech.jhipster.lite.module.domain.javabuild.ArtifactId;
import tech.jhipster.lite.module.domain.javabuild.GroupId;
import tech.jhipster.lite.module.domain.javabuild.VersionSlug;
import tech.jhipster.lite.module.domain.javaproperties.PropertyKey;
import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
import tech.jhipster.lite.shared.error.domain.Assert;

Expand All @@ -22,6 +23,7 @@ public class LangChain4JModuleFactory {
private static final VersionSlug VERSION_SLUG = versionSlug("langchain4j");

private static final String PROPERTIES = "properties";
private static final PropertyKey LANGCHAIN4J_PROPERTY_API_KEY = propertyKey("langchain4j.open-ai.chat-model.api-key");

public JHipsterModule buildModule(JHipsterModuleProperties properties) {
Assert.notNull(PROPERTIES, properties);
Expand All @@ -34,12 +36,15 @@ public JHipsterModule buildModule(JHipsterModuleProperties properties) {
.addDependency(GROUP_ID, OPEN_AI_ARTIFACT_ID, VERSION_SLUG)
.and()
.springMainProperties()
.set(propertyKey("langchain4j.open-ai.chat-model.api-key"), propertyValue("${OPENAI_API_KEY}"))
.comment(propertyKey("langchain4j.open-ai.chat-model.api-key"), comment(API_KEY_DEMO_COMMENT))
.set(LANGCHAIN4J_PROPERTY_API_KEY, propertyValue("${OPENAI_API_KEY}"))
.comment(LANGCHAIN4J_PROPERTY_API_KEY, comment(API_KEY_DEMO_COMMENT))
.set(propertyKey("langchain4j.open-ai.chat-model.model-name"), propertyValue("gpt-4o-mini"))
.set(propertyKey("langchain4j.open-ai.chat-model.log-requests"), propertyValue("true"))
.set(propertyKey("langchain4j.open-ai.chat-model.log-responses"), propertyValue("true"))
.and()
.springTestProperties()
.set(LANGCHAIN4J_PROPERTY_API_KEY, propertyValue("jhipster"))
.and()
.build();
//@formatter:on
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package tech.jhipster.lite.generator.server.springboot.mvc.sample.langchain4j.application;

import org.springframework.stereotype.Service;
import tech.jhipster.lite.generator.server.springboot.mvc.sample.langchain4j.domain.SampleLangChain4jModuleFactory;
import tech.jhipster.lite.module.domain.JHipsterModule;
import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;

@Service
public class SampleLangChain4jApplicationService {

private final SampleLangChain4jModuleFactory factory;

public SampleLangChain4jApplicationService() {
factory = new SampleLangChain4jModuleFactory();
}

public JHipsterModule buildModule(JHipsterModuleProperties properties) {
return factory.buildModule(properties);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package tech.jhipster.lite.generator.server.springboot.mvc.sample.langchain4j.domain;

import static tech.jhipster.lite.module.domain.JHipsterModule.*;

import tech.jhipster.lite.module.domain.JHipsterModule;
import tech.jhipster.lite.module.domain.file.JHipsterDestination;
import tech.jhipster.lite.module.domain.file.JHipsterSource;
import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
import tech.jhipster.lite.shared.error.domain.Assert;

public class SampleLangChain4jModuleFactory {

private static final String SAMPLE = "sample";

private static final JHipsterSource SOURCE = from("server/springboot/mvc/sample/langchain4j");

private static final String PRIMARY = "infrastructure/primary";

public JHipsterModule buildModule(JHipsterModuleProperties properties) {
Assert.notNull("properties", properties);

String packagePath = properties.packagePath();
JHipsterDestination mainDestination = toSrcMainJava().append(packagePath).append(SAMPLE);
JHipsterDestination testDestination = toSrcTestJava().append(packagePath).append(SAMPLE);

//@formatter:off
return moduleBuilder(properties)
.files()
.batch(SOURCE.append("main").append(PRIMARY), mainDestination.append(PRIMARY))
.addTemplate("ChatResource.java")
.and()
.batch(SOURCE.append("test").append(SAMPLE).append(PRIMARY), testDestination.append(PRIMARY))
.addTemplate("ChatResourceTest.java")
.and()
.and()
.build();
//@formatter:on
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package tech.jhipster.lite.generator.server.springboot.mvc.sample.langchain4j.infrastructure.primary;

import static tech.jhipster.lite.shared.slug.domain.JHLiteFeatureSlug.SPRING_MVC_SERVER;
import static tech.jhipster.lite.shared.slug.domain.JHLiteModuleSlug.*;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import tech.jhipster.lite.generator.server.springboot.mvc.sample.langchain4j.application.SampleLangChain4jApplicationService;
import tech.jhipster.lite.module.domain.resource.JHipsterModuleOrganization;
import tech.jhipster.lite.module.domain.resource.JHipsterModulePropertiesDefinition;
import tech.jhipster.lite.module.domain.resource.JHipsterModuleResource;

@Configuration
class SampleLangChain4jModuleConfiguration {

@Bean
JHipsterModuleResource langChain4jResourceInit(SampleLangChain4jApplicationService applicationService) {
return JHipsterModuleResource.builder()
.slug(SPRING_BOOT_LANGCHAIN4J_SAMPLE)
.propertiesDefinition(
JHipsterModulePropertiesDefinition.builder().addBasePackage().addIndentation().addSpringConfigurationFormat().build()
)
.apiDoc("Spring Boot - LangChain4j", "Add LangChain4j sample")
.organization(JHipsterModuleOrganization.builder().addDependency(SPRING_MVC_SERVER).addDependency(LANGCHAIN4J).build())
.tags("spring-boot", "spring", "server", "langchain4j")
.factory(applicationService::buildModule);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@tech.jhipster.lite.BusinessContext
package tech.jhipster.lite.generator.server.springboot.mvc.sample.langchain4j;
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ public enum JHLiteModuleSlug implements JHipsterModuleSlugFactory {
VUE_PINIA("vue-pinia"),
TS_PAGINATION_DOMAIN("ts-pagination-domain"),
TS_REST_PAGINATION("ts-rest-pagination"),
LANGCHAIN4J("langchain4j");
LANGCHAIN4J("langchain4j"),
SPRING_BOOT_LANGCHAIN4J_SAMPLE("spring-boot-langchain4j-sample");

private static final Map<String, JHLiteModuleSlug> moduleSlugMap = Stream.of(values()).collect(
Collectors.toMap(JHLiteModuleSlug::get, Function.identity())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package {{packageName}}.sample.infrastructure.primary;

import dev.langchain4j.model.chat.ChatLanguageModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
class ChatResource {
private final ChatLanguageModel chatLanguageModel;
public ChatResource(ChatLanguageModel chatLanguageModel) {
this.chatLanguageModel = chatLanguageModel;
}

@GetMapping("/chat")
public String send(@RequestParam(value = "message", defaultValue = "Hello") String message) {
return chatLanguageModel.generate(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package {{packageName}}.sample.infrastructure.primary;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;

import {{packageName}}.UnitTest;
import dev.langchain4j.model.chat.ChatLanguageModel;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@UnitTest
@ExtendWith(MockitoExtension.class)
class ChatResourceTest {
private static final String ANSWER = "Hello! How can I assist you today?";
@Mock
private ChatLanguageModel chatLanguageModel;
@InjectMocks
private ChatResource chat;
@Test
void shouldSendMessage() {
when(chatLanguageModel.generate("Hello")).thenReturn(ANSWER);
assertThat(chat.send("Hello")).isEqualTo(ANSWER);
}
}
7 changes: 7 additions & 0 deletions src/test/features/server/springboot/langchain4j.feature
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,10 @@ Feature: LangChain4j module
Then I should have entries in "src/main/resources/config/application.yml"
| open-ai |
| langchain4j |

Scenario: Should add Spring Boot LangChain4j Sample
When I apply "spring-boot-langchain4j-sample" module to default project with maven file
| packageName | tech.jhipster.chips |
| baseName | jhipster |
Then I should have files in "src/main/java/tech/jhipster/chips/sample/infrastructure/primary"
| ChatResource.java |
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ void shouldCreateModule() {
log-responses: 'true'
model-name: gpt-4o-mini
"""
)
.and()
.hasFile("src/test/resources/config/application-test.yml")
.containing(
"""
langchain4j:
open-ai:
chat-model:
api-key: jhipster
"""
);
}
}
11 changes: 10 additions & 1 deletion tests-ci/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,17 @@ elif [[ $application == 'thymeleafapp' ]]; then
"htmx-webjars" \
"thymeleaf-template-htmx-webjars"

elif [[ $application == 'langchain4japp' ]]; then
init_server
spring_boot_mvc
sonar_back

applyModules \
"langchain4j" \
"spring-boot-langchain4j-sample"

else
echo "*** Unknown configuration..."
echo "*** Unknown configuration..." "$application"
exit 1
fi

Expand Down

0 comments on commit 28abff4

Please sign in to comment.