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

Commit

Permalink
[cli] Add Commands to create an application from an archetype (LangSt…
Browse files Browse the repository at this point in the history
  • Loading branch information
eolivelli authored Nov 6, 2023
1 parent 586bc4c commit ace7809
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import ai.langstream.admin.client.model.Archetypes;
import ai.langstream.admin.client.util.MultiPartBodyPublisher;
import ai.langstream.admin.client.util.Slf4jLAdminClientLogger;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
Expand All @@ -44,6 +45,8 @@ public class AdminClient implements AutoCloseable {
private final AdminClientLogger logger;
private final HttpClientFacade httpClientFacade;

private static final ObjectMapper mapper = new ObjectMapper();

public AdminClient(AdminClientConfiguration adminClientConfiguration) {
this(adminClientConfiguration, new Slf4jLAdminClientLogger(log));
}
Expand Down Expand Up @@ -183,6 +186,20 @@ public String tenantAppPath(String uri) {
return String.format("/applications/%s%s", tenant, uri);
}

public String tenantArchetypesPath(String uri) {
final String tenant = configuration.getTenant();
if (tenant == null) {
throw new IllegalStateException(
"Tenant not set. Please set the tenant in the configuration.");
}
logger.debug(String.format("Using tenant: %s", tenant));
return String.format("/archetypes/%s%s", tenant, uri);
}

public String tenantArchetypePath(String archetypeId, String uri) {
return tenantArchetypesPath(String.format("/%s%s", archetypeId, uri));
}

static String formatQueryString(Map<String, String> params) {
if (params == null || params.isEmpty()) {
return "";
Expand Down Expand Up @@ -213,15 +230,13 @@ private class ArchetypesImpl implements Archetypes {
@Override
@SneakyThrows
public String list() {
final String tenant = configuration.getTenant();
return http(newGet(String.format("/archetypes/%s", tenant))).body();
return http(newGet(tenantArchetypesPath(""))).body();
}

@Override
@SneakyThrows
public String get(String archetype) {
final String tenant = configuration.getTenant();
return http(newGet(String.format("/archetypes/%s/%s", tenant, archetype))).body();
return http(newGet(tenantArchetypesPath("/" + archetype))).body();
}
}

Expand All @@ -244,6 +259,25 @@ public String deploy(
return http(request).body();
}

@Override
@SneakyThrows
public String deployFromArchetype(
String applicationId,
String archetypeId,
Map<String, Object> parameters,
boolean dryRun) {
// /api/archetypes/my-tenant/simple/applications/app-id
final String path =
tenantArchetypePath(archetypeId, "/applications/" + applicationId)
+ "?dry-run="
+ dryRun;
final String contentType = "application/json";
final String parametersJson = mapper.writeValueAsString(parameters);
final HttpRequest request =
newPost(path, contentType, HttpRequest.BodyPublishers.ofString(parametersJson));
return http(request).body();
}

@Override
@SneakyThrows
public void update(String application, MultiPartBodyPublisher multiPartBodyPublisher) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.io.InputStream;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Map;

public interface Applications {
String deploy(String application, MultiPartBodyPublisher multiPartBodyPublisher);
Expand Down Expand Up @@ -49,4 +50,7 @@ <T> HttpResponse<T> download(
String getCodeInfo(String application, String codeArchiveId);

HttpResponse<InputStream> logs(String application, List<String> filter, String format);

String deployFromArchetype(
String name, String archetypeId, Map<String, Object> parameters, boolean dryRun);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@
*/
package ai.langstream.cli.commands;

import ai.langstream.cli.commands.archetypes.CreateAppFromArchetypeCmd;
import ai.langstream.cli.commands.archetypes.GetArchetypeCmd;
import ai.langstream.cli.commands.archetypes.ListArchetypesCmd;
import lombok.Getter;
import picocli.CommandLine;

@CommandLine.Command(
name = "archetypes",
header = "Use ${ROOT-COMMAND-NAME} Archetypes",
subcommands = {ListArchetypesCmd.class})
subcommands = {
ListArchetypesCmd.class,
GetArchetypeCmd.class,
CreateAppFromArchetypeCmd.class
})
@Getter
public class RootArchetypeCmd {
@CommandLine.ParentCommand private RootCmd rootCmd;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* 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.cli.commands.archetypes;

import ai.langstream.admin.client.AdminClient;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import lombok.SneakyThrows;
import picocli.CommandLine;

@CommandLine.Command(
name = "create-application",
header = "Create an application from an archetype")
public class CreateAppFromArchetypeCmd extends BaseArchetypeCmd {

private static final ObjectMapper mapper = new ObjectMapper();

@CommandLine.Parameters(description = "Name of the application")
private String name;

@CommandLine.Option(
names = {"-a", "--archetype"},
description = "Id of the archetype to use")
private String archetypeId;

@CommandLine.Option(
names = {"-pf", "--parameters-file"},
description = "File that contains the parameters to use for the application")
private String parametersFile;

@CommandLine.Option(
names = {"-p", "--parameters"},
description = "Parameters, in JSON format")
private String parameters;

@CommandLine.Option(
names = {"--dry-run"},
description =
"Dry-run mode. Do not deploy the application but only resolves placeholders and display the result.")
private boolean dryRun;

@CommandLine.Option(
names = {"-o"},
description =
"Output format for dry-run mode. Formats are: yaml, json. Default value is yaml.")
private Formats format = Formats.yaml;

Formats format() {
ensureFormatIn(format, Formats.json, Formats.yaml);
return format;
}

@Override
@SneakyThrows
public void run() {
Map<String, Object> parametersFromFile = readParametersFromFile(parametersFile);
Map<String, Object> parametersFromCommandLine = readParameters(parameters);
Map<String, Object> finalParameters = new HashMap<>();
finalParameters.putAll(parametersFromFile);
finalParameters.putAll(parametersFromCommandLine);

AdminClient client = getClient();
// verify archetype exists
client.archetypes().get(archetypeId);

final String response =
client.applications()
.deployFromArchetype(name, archetypeId, finalParameters, dryRun);
if (dryRun) {
final Formats format = format();
print(format == Formats.raw ? Formats.yaml : format, response, null, null);
} else {
log(String.format("application %s deployed", name));
}
}

private Map<String, Object> readParametersFromFile(String parametersFile) {
if (parametersFile == null || parametersFile.isEmpty()) {
return Map.of();
}
final File file = new File(parametersFile);
if (!file.exists()) {
throw new IllegalArgumentException("File " + parametersFile + " does not exist");
}
try {
return mapper.readValue(file, new TypeReference<Map<String, Object>>() {});
} catch (IOException err) {
throw new IllegalArgumentException(
"Cannot parse parameters file "
+ file.getAbsolutePath()
+ ": "
+ err.getMessage(),
err);
}
}

private Map<String, Object> readParameters(String parameters) {
if (parameters == null || parameters.isEmpty()) {
return Map.of();
}
try {
return mapper.readValue(parameters, new TypeReference<Map<String, Object>>() {});
} catch (IOException err) {
throw new IllegalArgumentException(
"Cannot parse parameters " + parameters + ": " + err.getMessage(), err);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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.cli.commands.archetypes;

import lombok.SneakyThrows;
import picocli.CommandLine;

@CommandLine.Command(name = "get", header = "Get the metadata about an archetype")
public class GetArchetypeCmd extends BaseArchetypeCmd {

@CommandLine.Option(
names = {"-a", "--archetype"},
description = "Id of the archetype to get")
private String archetypeId;

@Override
@SneakyThrows
public void run() {
final String body = getClient().archetypes().get(archetypeId);
log(body);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ public static BiFunction<JsonNode, String, Object> getRawFormatValuesSupplier()
return (jsonNode, s) -> {
switch (s) {
case "id":
return searchValueInJson(jsonNode, "archetype.id");
return searchValueInJson(jsonNode, "id");
case "labels":
return searchValueInJson(jsonNode, "archetype.labels");
return searchValueInJson(jsonNode, "labels");
default:
return jsonNode.get(s);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,33 @@
secrets:
- id: kafka
data:
username: "${KAFKA_USERNAME:-}"
password: "${KAFKA_PASSWORD:-}"
tenant: "${KAFKA_TENANT:-}"
bootstrap-servers: "${KAFKA_BOOTSTRAP_SERVERS:-}"
username: ""
password: ""
tenant: ""
bootstrap-servers: ""
- id: open-ai
data:
access-key: "${OPEN_AI_ACCESS_KEY:-}"
url: "${OPEN_AI_URL:-}"
provider: "${OPEN_AI_PROVIDER:-openai}"
embeddings-model: "${OPEN_AI_EMBEDDINGS_MODEL:-text-embedding-ada-002}"
chat-completions-model: "${OPEN_AI_CHAT_COMPLETIONS_MODEL:-gpt-3.5-turbo}"
text-completions-model: "${OPEN_AI_TEXT_COMPLETIONS_MODEL:-gpt-3.5-turbo-instruct}"
access-key: ""
url: ""
provider: "openai"
embeddings-model: ""
chat-completions-model: ""
text-completions-model: ""
- id: astra
data:
clientId: ${ASTRA_CLIENT_ID:-}
secret: ${ASTRA_SECRET:-}
token: ${ASTRA_TOKEN:-}
database: ${ASTRA_DATABASE:-}
clientId: ""
secret: ""
token: ""
database: ""
- id: s3
data:
bucket-name: "${S3_BUCKET_NAME:-documents}"
access-key: "${S3_ACCESS_KEY:-minioadmin}"
secret: "${S3_SECRET:-minioadmin}"
region: "${S3_REGION:-}"
bucket-name: ""
access-key: ""
secret: ""
region: ""
- id: google
data:
client-id: "${GOOGLE_CLIENT_ID:-}"
client-id: ""
- id: github
data:
client-id: "${GITHUB_CLIENT_ID:-}"
client-id: ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"website-url": ["https://docs.langstream.ai"],
"website-crawler-depth": 2,
"embeddings-model": "some-model",
"access-key": "sdfsf",
"chat-completion-model":"chat-completion-model",
"llm-prompt": [
{ "role": "system",
"content": "bla bla bla"},
{ "role": "user",
"content": "{{ value.question }}"}
],
"astra-database": "some database",
"astra-client-id": "dsfdf",
"astra-client-secret": "fsfsdsfs",
"astra-token": "sdfsdfs",
"s3-bucket": "busdfsdfsf",
"s3-access-key": "sdfsdf",
"kafka-bootstrap-servers": "localhost:9092",
"kafka-username": "sdfsdf",
"kafka-password": "kafsdfffdf"
}

0 comments on commit ace7809

Please sign in to comment.