Skip to content

Commit 06f0af9

Browse files
committed
Adapt tests in OpenAI module to run with OpenAI API
- Add a test profile for working with real LLM in cloud, without mocks - Conditionally enable tests, which don't work with Mock GPT - Adapt verification to verify both answers from Mock GPT and OpenAI - Use the same model for all tests - Use less tokens, especially for answers - Link non-working tests to issues
1 parent 439c972 commit 06f0af9

15 files changed

+109
-50
lines changed

integration-tests/openai/src/main/java/org/acme/example/openai/MessageUtil.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,23 @@
22

33
import java.util.Collections;
44

5+
import org.eclipse.microprofile.config.ConfigProvider;
6+
57
import dev.langchain4j.model.openai.internal.chat.ChatCompletionRequest;
68
import dev.langchain4j.model.openai.internal.completion.CompletionRequest;
79
import dev.langchain4j.model.openai.internal.embedding.EmbeddingRequest;
810

911
public class MessageUtil {
1012

13+
static String model;
14+
15+
static {
16+
model = ConfigProvider.getConfig().getValue("quarkus.langchain4j.openai.chat-model.model-name", String.class);
17+
}
18+
1119
public static CompletionRequest createCompletionRequest(String prompt) {
1220
return CompletionRequest.builder()
13-
.model("gpt-3.5-turbo")
21+
.model(model)
1422
.logitBias(Collections.emptyMap())
1523
.maxTokens(100)
1624
.user("testing")
@@ -22,7 +30,7 @@ public static CompletionRequest createCompletionRequest(String prompt) {
2230

2331
public static ChatCompletionRequest createChatCompletionRequest(String userMessage) {
2432
return ChatCompletionRequest.builder()
25-
.model("gpt-3.5-turbo")
33+
.model(model)
2634
.logitBias(Collections.emptyMap())
2735
.maxTokens(100)
2836
.user("testing")

integration-tests/openai/src/main/java/org/acme/example/openai/QuarkusRestApiResource.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ public QuarkusRestApiResource(LangChain4jOpenAiConfig runtimeConfig)
6464
@Path("chat/sync")
6565
public String chatSync() {
6666
return restApi.blockingChatCompletion(
67-
createChatCompletionRequest("Write a short 1 paragraph funny poem about segmentation fault"),
67+
createChatCompletionRequest(
68+
"Which one of these languages is more susceptible to segmentation fault: Java, Go, C++ or Rust?"),
6869
OpenAiRestApi.ApiMetadata.builder()
6970
.openAiApiKey(token)
7071
.organizationId(organizationId)
@@ -76,7 +77,7 @@ public String chatSync() {
7677
@Path("chat/async")
7778
public Uni<String> chatAsync() {
7879
return restApi
79-
.createChatCompletion(createChatCompletionRequest("Write a short 1 paragraph funny poem about Unicode"),
80+
.createChatCompletion(createChatCompletionRequest("Write a short definition of Unicode"),
8081
OpenAiRestApi.ApiMetadata.builder()
8182
.openAiApiKey(token)
8283
.organizationId(organizationId)
@@ -122,7 +123,8 @@ public Multi<String> chatStreaming() {
122123
@Path("language/sync")
123124
public String languageSync() {
124125
return restApi.blockingCompletion(
125-
createCompletionRequest("Write a short 1 paragraph funny poem about segmentation fault"),
126+
createCompletionRequest(
127+
"Which one of these languages is more susceptible to segmentation fault: Java, Go, C++ or Rust?"),
126128
OpenAiRestApi.ApiMetadata.builder()
127129
.openAiApiKey(token)
128130
.organizationId(organizationId)
@@ -134,7 +136,7 @@ public String languageSync() {
134136
@Path("language/async")
135137
public Uni<String> languageAsync() {
136138
return restApi
137-
.completion(createCompletionRequest("Write a short 1 paragraph funny poem about Unicode"),
139+
.completion(createCompletionRequest("Write a short definition of Unicode"),
138140
OpenAiRestApi.ApiMetadata.builder()
139141
.openAiApiKey(token)
140142
.organizationId(organizationId)

integration-tests/openai/src/main/java/org/acme/example/openai/chat/ChatLanguageModelResource.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import jakarta.ws.rs.Path;
1515
import jakarta.ws.rs.core.MediaType;
1616

17+
import org.eclipse.microprofile.config.inject.ConfigProperty;
1718
import org.jboss.resteasy.reactive.RestStreamElementType;
1819

1920
import dev.langchain4j.data.message.AiMessage;
@@ -38,6 +39,9 @@ public class ChatLanguageModelResource {
3839
private final ChatModel chatLanguageModel;
3940
private final StreamingChatModel streamingChatLanguageModel;
4041

42+
@ConfigProperty(name = "quarkus.langchain4j.openai.chat-model.model-name")
43+
String modelName;
44+
4145
public ChatLanguageModelResource(ChatModel chatLanguageModel,
4246
StreamingChatModel streamingChatLanguageModel) {
4347
this.chatLanguageModel = chatLanguageModel;
@@ -96,14 +100,13 @@ public String template() {
96100
@Path("memory")
97101
public String memory() throws Exception {
98102

99-
TokenCountEstimator tokenizer = new OpenAiTokenCountEstimator("gpt-3.5-turbo");
103+
TokenCountEstimator tokenizer = new OpenAiTokenCountEstimator(modelName);
100104
ChatMemory chatMemory = TokenWindowChatMemory.withMaxTokens(1000, tokenizer);
101105

102106
StringBuffer sb = new StringBuffer();
103107

104108
UserMessage userMessage1 = userMessage(
105-
"How do I optimize database queries for a large-scale e-commerce platform? "
106-
+ "Answer short in three to five lines maximum.");
109+
"Who is the original creator of Linux operation system?");
107110
chatMemory.add(userMessage1);
108111

109112
sb.append("[User]: ")
@@ -138,9 +141,7 @@ public void onError(Throwable throwable) {
138141
AiMessage firstAiMessage = futureRef.get().get(60, TimeUnit.SECONDS);
139142
chatMemory.add(firstAiMessage);
140143

141-
UserMessage userMessage2 = userMessage(
142-
"Give a concrete example implementation of the first point? " +
143-
"Be short, 10 lines of code maximum.");
144+
UserMessage userMessage2 = userMessage("In which country was he born?");
144145
chatMemory.add(userMessage2);
145146

146147
sb.append("\n\n[User]: ")

integration-tests/openai/src/main/java/org/acme/example/openai/embedding/EmbeddingModelResource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ public EmbeddingModelResource(EmbeddingModel embeddingModel) {
2626
@Path("blocking")
2727
@Produces("text/plain")
2828
public List<Float> blocking() {
29-
return embeddingModel.embed("This is some text").content().vectorAsList();
29+
return embeddingModel.embed("What is the last name of the author of Linux?").content().vectorAsList();
3030
}
3131
}

integration-tests/openai/src/main/resources/application.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ openai.key=ADD_A_TOKEN
22
%test.openai.key=ADD_A_TOKEN
33
%test.quarkus.langchain4j.openai.base-url= https://mockgpt.wiremockapi.cloud/v1
44

5+
%cloud-llm.quarkus.langchain4j.openai.base-url=https://api.openai.com/v1
6+
%cloud-llm.quarkus.langchain4j.openai.chat-model.model-name=gpt-4o-mini
7+
58
quarkus.langchain4j.openai.api-key=${openai.key}
69
quarkus.langchain4j.openai.chat-model.model-name=gpt-4o
710
quarkus.langchain4j.timeout=60s

integration-tests/openai/src/test/java/org/acme/example/openai/OpenAiRestApiResourceTest.java

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

33
import static io.restassured.RestAssured.given;
44
import static org.assertj.core.api.Assertions.assertThat;
5-
import static org.hamcrest.Matchers.containsString;
65

76
import java.net.URL;
87
import java.util.ArrayList;
@@ -37,7 +36,7 @@ public void chatSync() {
3736
.get("chat/sync")
3837
.then()
3938
.statusCode(200)
40-
.body(containsString("MockGPT"));
39+
.body(TestUtils.containsStringOrMock("C++"));
4140
}
4241

4342
@Test
@@ -48,7 +47,7 @@ public void chatAsync() {
4847
.get("chat/async")
4948
.then()
5049
.statusCode(200)
51-
.body(containsString("MockGPT"));
50+
.body(TestUtils.containsStringOrMock("Unicode"));
5251
}
5352

5453
@Test
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.acme.example.openai;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
import org.eclipse.microprofile.config.ConfigProvider;
7+
import org.eclipse.microprofile.config.inject.ConfigProperty;
8+
import org.hamcrest.Matcher;
9+
import org.hamcrest.Matchers;
10+
11+
public class TestUtils {
12+
private static final String MOCK_ANSWER = "MockGPT";
13+
14+
@ConfigProperty(name = "openai.key")
15+
static String key;
16+
17+
static {
18+
key = ConfigProvider.getConfig().getValue("openai.key", String.class);
19+
}
20+
21+
private static boolean useMock() {
22+
return key.equalsIgnoreCase("ADD_A_TOKEN");
23+
}
24+
25+
public static Matcher<String> containsStringOrMock(String... expected) {
26+
if (useMock()) {
27+
return Matchers.containsString(MOCK_ANSWER);
28+
} else {
29+
// due to peculiarities of java generics, anyOf method would not accept List<Matcher<String>>
30+
List<Matcher<? super String>> possibilities = new ArrayList<>(expected.length);
31+
for (String value : expected) {
32+
possibilities.add(Matchers.containsStringIgnoringCase(value));
33+
}
34+
return Matchers.anyOf(possibilities);
35+
}
36+
}
37+
}

integration-tests/openai/src/test/java/org/acme/example/openai/aiservices/AssistantResourceTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package org.acme.example.openai.aiservices;
22

33
import static io.restassured.RestAssured.given;
4-
import static org.hamcrest.Matchers.containsString;
54

65
import java.net.URL;
76

7+
import org.acme.example.openai.TestUtils;
88
import org.junit.jupiter.api.Test;
99

1010
import io.quarkus.test.common.http.TestHTTPEndpoint;
@@ -22,10 +22,10 @@ public class AssistantResourceTest {
2222
public void get() {
2323
given()
2424
.baseUri(url.toString())
25-
.queryParam("message", "This is a test")
25+
.queryParam("message", "Hello, my name is Quarkus")
2626
.get()
2727
.then()
2828
.statusCode(200)
29-
.body(containsString("MockGPT"));
29+
.body(TestUtils.containsStringOrMock("Quarkus"));
3030
}
3131
}

integration-tests/openai/src/test/java/org/acme/example/openai/aiservices/AssistantResourceWithGuardrailsAndObservabilityTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
import static io.restassured.RestAssured.get;
44
import static org.assertj.core.api.Assertions.assertThat;
55
import static org.awaitility.Awaitility.await;
6-
import static org.hamcrest.Matchers.containsString;
76

87
import java.time.Duration;
98

109
import jakarta.inject.Inject;
1110

11+
import org.acme.example.openai.TestUtils;
1212
import org.acme.example.openai.aiservices.AssistantResourceWithGuardrailsAndObservability.AbstractIGImplementingValidateWithParams;
1313
import org.acme.example.openai.aiservices.AssistantResourceWithGuardrailsAndObservability.AbstractIGImplementingValidateWithUserMessage;
1414
import org.acme.example.openai.aiservices.AssistantResourceWithGuardrailsAndObservability.AbstractOGImplementingValidateWithAiMessage;
@@ -41,7 +41,7 @@ static void addSimpleRegistry() {
4141
void guardrailMetricsAvailable() {
4242
get("/assistant-with-guardrails-observability").then()
4343
.statusCode(200)
44-
.body(containsString("MockGPT"));
44+
.body(TestUtils.containsStringOrMock("test"));
4545

4646
await()
4747
.atMost(Duration.ofSeconds(10))

integration-tests/openai/src/test/java/org/acme/example/openai/aiservices/AssistantResourceWithMetricsTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package org.acme.example.openai.aiservices;
22

33
import static io.restassured.RestAssured.given;
4-
import static org.hamcrest.Matchers.containsString;
54
import static org.junit.jupiter.api.Assertions.fail;
65

76
import java.net.URL;
87
import java.util.Collection;
98

109
import jakarta.inject.Inject;
1110

11+
import org.acme.example.openai.TestUtils;
1212
import org.junit.jupiter.api.BeforeAll;
1313
import org.junit.jupiter.api.Test;
1414

@@ -41,7 +41,7 @@ public void noMicrometerAnnotations() throws InterruptedException {
4141
.get("a1")
4242
.then()
4343
.statusCode(200)
44-
.body(containsString("MockGPT"));
44+
.body(TestUtils.containsStringOrMock("test"));
4545

4646
waitForMeters(
4747
registry.find("langchain4j.aiservices.timed")
@@ -66,7 +66,7 @@ public void micrometerAnnotationOnClass() throws InterruptedException {
6666
.get("a2")
6767
.then()
6868
.statusCode(200)
69-
.body(containsString("MockGPT"));
69+
.body(TestUtils.containsStringOrMock("test"));
7070

7171
waitForMeters(
7272
registry.find("langchain4j.aiservices.timed")
@@ -92,7 +92,7 @@ public void micrometerAnnotationOnMethod() throws InterruptedException {
9292
.get("a2c2")
9393
.then()
9494
.statusCode(200)
95-
.body(containsString("MockGPT"));
95+
.body(TestUtils.containsStringOrMock("test"));
9696

9797
waitForMeters(registry.find("a2c2-timed").timers(), 1);
9898
waitForMeters(registry.find("a2c2-counted")

0 commit comments

Comments
 (0)