Skip to content
This repository has been archived by the owner on Aug 25, 2024. It is now read-only.

Commit

Permalink
[tests] Add ai-chat-completions e2e test (LangStream#430)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoloboschi authored Sep 18, 2023
1 parent c48ebde commit 9ce7e36
Show file tree
Hide file tree
Showing 10 changed files with 555 additions and 239 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ jobs:
uname -m
./dev/prepare-minikube-for-e2e-tests.sh
./mvnw install -pl langstream-e2e-tests -am -DskipTests
./mvnw verify -pl langstream-e2e-tests -De2eTests
./mvnw verify -pl langstream-e2e-tests -De2eTests -DexcludedGroups="needs-credentials"
- name: Upload Surefire reports
uses: actions/upload-artifact@v3
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

import java.nio.file.Paths;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import lombok.SneakyThrows;
Expand Down Expand Up @@ -47,40 +47,16 @@ public void removeCassandra() {
public void test() throws Exception {
installLangStreamCluster(false);
final String tenant = "ten-" + System.currentTimeMillis();
executeCommandOnClient(
"""
bin/langstream tenants put %s &&
bin/langstream configure tenant %s"""
.formatted(tenant, tenant)
.replace(System.lineSeparator(), " ")
.split(" "));
String testAppsBaseDir = "src/test/resources/apps";
String testInstanceBaseDir = "src/test/resources/instances";
String testSecretBaseDir = "src/test/resources/secrets";
setupTenant(tenant);
final String applicationId = "my-test-app";
String cassandraHost = "cassandra-0.cassandra." + namespace;
copyFileToClientContainer(
Paths.get(testAppsBaseDir, "cassandra-sink").toFile(), "/tmp/cassandra-sink");
copyFileToClientContainer(
Paths.get(testInstanceBaseDir, "kafka-kubernetes.yaml").toFile(),
"/tmp/instance.yaml");
copyFileToClientContainer(
Paths.get(testSecretBaseDir, "secret1.yaml").toFile(),
"/tmp/secrets.yaml",
file ->
file.replace("CASSANDRA-HOST-INJECTED", cassandraHost)
.replace("CASSANDRA-LOCAL-DC-INJECTED", "datacenter1")
.replace("CASSANDRA-PORT-INJECTED", "9042"));

executeCommandOnClient(
"bin/langstream apps deploy %s -app /tmp/cassandra-sink -i /tmp/instance.yaml -s /tmp/secrets.yaml"
.formatted(applicationId)
.split(" "));
client.apps()
.statefulSets()
.inNamespace(TENANT_NAMESPACE_PREFIX + tenant)
.withName(applicationId + "-module-1-pipeline-1-sink-1")
.waitUntilReady(4, TimeUnit.MINUTES);
final Map<String, String> env =
Map.of(
"CASSANDRA_CONTACT_POINTS", cassandraHost,
"CASSANDRA_LOCAL_DC", "datacenter1",
"CASSANDRA_PORT", "9042");
deployLocalApplication(applicationId, "cassandra-sink", env);
awaitApplicationReady(applicationId, 1);

executeCommandOnClient(
"bin/langstream gateway produce %s produce-input -v '{\"id\": 10, \"name\": \"test-from-sink\", \"description\": \"test-from-sink\"}'"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright DataStax, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ai.langstream.tests;

import ai.langstream.tests.util.ConsumeGatewayMessage;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@Slf4j
@ExtendWith(BaseEndToEndTest.class)
@Tag(BaseEndToEndTest.CATEGORY_NEEDS_CREDENTIALS)
public class ChatCompletionsIT extends BaseEndToEndTest {

static Map<String, String> appEnv;

@BeforeAll
public static void checkCredentials() {
appEnv =
getAppEnvMapFromSystem(
List.of(
"OPEN_AI_ACCESS_KEY",
"OPEN_AI_URL",
"OPEN_AI_CHAT_COMPLETIONS_MODEL",
"OPEN_AI_PROVIDER"));
}

@Test
public void test() throws Exception {
installLangStreamCluster(false);
final String tenant = "ten-" + System.currentTimeMillis();
setupTenant(tenant);

final String applicationId = "app";

deployLocalApplication(applicationId, "chat-completions", appEnv);
awaitApplicationReady(applicationId, 1);

final String sessionId = UUID.randomUUID().toString();

executeCommandOnClient(
"bin/langstream gateway produce %s produce-input -v 'Who was the first president of the United States?' -p sessionId=%s"
.formatted(applicationId, sessionId)
.split(" "));

final ConsumeGatewayMessage message =
consumeOneMessageFromGateway(
applicationId,
"consume-history",
"-p sessionId=%s --position earliest --connect-timeout 30"
.formatted(sessionId)
.split(" "));
log.info("Output: {}", message);
Assertions.assertTrue(message.getAnswerFromChatCompletionsValue().contains("Washington"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import ai.langstream.deployer.k8s.api.crds.agents.AgentCustomResource;
import ai.langstream.deployer.k8s.api.crds.apps.ApplicationCustomResource;
import io.fabric8.kubernetes.api.model.Secret;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -35,35 +34,11 @@ public class PythonFunctionIT extends BaseEndToEndTest {
public void test() {
installLangStreamCluster(false);
final String tenant = "ten-" + System.currentTimeMillis();
executeCommandOnClient(
"""
bin/langstream tenants put %s &&
bin/langstream configure tenant %s"""
.formatted(tenant, tenant)
.replace(System.lineSeparator(), " ")
.split(" "));
String testAppsBaseDir = "src/test/resources/apps";
String testInstanceBaseDir = "src/test/resources/instances";
String testSecretBaseDir = "src/test/resources/secrets";
setupTenant(tenant);
final String applicationId = "my-test-app";
copyFileToClientContainer(
Paths.get(testAppsBaseDir, "python-processor").toFile(), "/tmp/python-processor");
copyFileToClientContainer(
Paths.get(testInstanceBaseDir, "kafka-kubernetes.yaml").toFile(),
"/tmp/instance.yaml");
copyFileToClientContainer(
Paths.get(testSecretBaseDir, "secret1.yaml").toFile(), "/tmp/secrets.yaml");

executeCommandOnClient(
"bin/langstream apps deploy %s -app /tmp/python-processor -i /tmp/instance.yaml -s /tmp/secrets.yaml"
.formatted(applicationId)
.split(" "));
deployLocalApplication(applicationId, "python-processor");
final String tenantNamespace = TENANT_NAMESPACE_PREFIX + tenant;
client.apps()
.statefulSets()
.inNamespace(tenantNamespace)
.withName(applicationId + "-test-python-processor")
.waitUntilReady(4, TimeUnit.MINUTES);
awaitApplicationReady(applicationId, 1);

executeCommandOnClient(
"bin/langstream gateway produce %s produce-input -v my-value --connect-timeout 30"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright DataStax, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ai.langstream.tests.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ConsumeGatewayMessage {
protected static final ObjectMapper JSON_MAPPER = new ObjectMapper();

@SneakyThrows
public static ConsumeGatewayMessage readValue(String line) {
return JSON_MAPPER.readValue(line, ConsumeGatewayMessage.class);
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public static final class Record {
private Object key;
private Object value;
private Map<String, String> headers;
}

private Record record;
private String offset;

@SneakyThrows
public String getAnswerFromChatCompletionsValue() {
final Map<String, Object> chatHistoryModel =
JSON_MAPPER.readValue((String) record.getValue(), Map.class);
final String answer = chatHistoryModel.get("answer").toString();
return answer;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#
#
# Copyright DataStax, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

configuration:
resources:
- type: "open-ai-configuration"
name: "OpenAI Azure configuration"
configuration:
url: "{{ secrets.open-ai.url }}"
access-key: "{{ secrets.open-ai.access-key }}"
provider: "{{ secrets.open-ai.provider }}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#
#
# Copyright DataStax, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

gateways:
- id: produce-input
type: produce
topic: input-topic
parameters:
- sessionId
produce-options:
headers:
- key: langstream-client-session-id
value-from-parameters: sessionId

- id: consume-history
type: consume
topic: history-topic
parameters:
- sessionId
consume-options:
filters:
headers:
- key: langstream-client-session-id
value-from-parameters: sessionId

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#
# Copyright DataStax, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

topics:
- name: "input-topic"
creation-mode: create-if-not-exists
- name: "output-topic"
creation-mode: create-if-not-exists
- name: "history-topic"
creation-mode: create-if-not-exists
pipeline:
- name: "convert-to-json"
type: "document-to-json"
input: "input-topic"
configuration:
text-field: "question"
- name: "ai-chat-completions"
type: "ai-chat-completions"
output: "history-topic"
configuration:
model: "{{{secrets.open-ai.chat-completions-model}}}"
completion-field: "value.answer"
log-field: "value.prompt"
stream-to-topic: "output-topic"
stream-response-completion-field: "value"
min-chunks-per-message: 20
messages:
- role: user
content: "You are an helpful assistant. Below you can fine a question from the user. Please try to help them the best way you can.\n\n{{% value.question}}"
13 changes: 10 additions & 3 deletions langstream-e2e-tests/src/test/resources/secrets/secret1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ secrets:
- name: cassandra
id: cassandra
data:
contact-points: CASSANDRA-HOST-INJECTED
local-dc: CASSANDRA-LOCAL-DC-INJECTED
port: CASSANDRA-PORT-INJECTED
contact-points: "${CASSANDRA_CONTACT_POINTS}"
local-dc: "${CASSANDRA_LOCAL_DC}"
port: "${CASSANDRA_PORT}"
- id: open-ai
data:
access-key: "${OPEN_AI_ACCESS_KEY}"
url: "${OPEN_AI_URL}"
provider: "${OPEN_AI_PROVIDER}"
embeddings-model: "${OPEN_AI_EMBEDDINGS_MODEL}"
chat-completions-model: "${OPEN_AI_CHAT_COMPLETIONS_MODEL}"

0 comments on commit 9ce7e36

Please sign in to comment.