diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index f32820d..1539ce6 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -20,6 +20,19 @@ jobs: distribution: 'temurin' cache: maven + - name: Generate settings.xml + run: | + mkdir -p ~/.m2 + echo " + + + github + ${{ github.actor }} + ${{ secrets.GITHUB_TOKEN }} + + + " > ~/.m2/settings.xml + - name: Download dependencies run: | set -x @@ -39,20 +52,6 @@ jobs: echo "default-key $(gpg --list-keys --with-colons | grep pub | cut -d':' -f5)" >> ~/.gnupg/gpg.conf echo "pinentry-mode loopback" >> ~/.gnupg/gpg.conf - - name: Generate settings.xml - if: github.ref == 'refs/heads/main' - run: | - mkdir -p ~/.m2 - echo " - - - github - ${{ github.actor }} - ${{ secrets.GITHUB_TOKEN }} - - - " > ~/.m2/settings.xml - - name: Deploy Snapshot (only for main) if: github.ref == 'refs/heads/main' run: | diff --git a/acorn-graphql/README.md b/acorn-graphql/README.md deleted file mode 100644 index d7b93e1..0000000 --- a/acorn-graphql/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Acorn GraphQL - -This module contains the core implementation classes and interfaces of Acorn. This base module is used by framework specific modules and can be used as the basis for specific implementations for frameworks or LLMs. - -Specific implementation that utilize this module need to implement two things: -1. An implementation of `APIExecutor` for executing GraphQL queries -2. Passing the generated `APIFunction` to the LLM and validating/executing them when invoked by the LLM. This usually requires a thin wrapper based on the LLM framework or SDK used. - -This module consists of the following packages: - -* [Tool](src/main/java/com/datasqrl/ai/tool): Defines the `APIFunction` class which represents and LLM tool and how it maps to GraphQL API queries. Also defines `Context` for sensitive information sandboxing. -* [Converter](src/main/java/com/datasqrl/ai/converter): Converts a provided GraphQL schema or individual GraphQL operations to `APIFunction`. -* [API](src/main/java/com/datasqrl/ai/api): Interfaces and methods for API invocation. -* [Chat](src/main/java/com/datasqrl/ai/chat): Saving and retrieving messages from GraphQL API. -* [Util](src/main/java/com/datasqrl/ai/util): Utility classes/methods used across packages. - diff --git a/acorn-graphql/pom.xml b/acorn-graphql/pom.xml deleted file mode 100644 index 5249672..0000000 --- a/acorn-graphql/pom.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - 4.0.0 - - com.datasqrl.acorn - acorn-parent - 0.2-SNAPSHOT - - - acorn-graphql - acorn - GraphQL API Module - Acorn Graphql - - - - - com.fasterxml.jackson.core - jackson-core - ${jackson.version} - - - com.fasterxml.jackson.core - jackson-databind - ${jackson.version} - - - - - com.graphql-java - graphql-java - ${graphql-java.version} - - - com.graphql-java - graphql-java-extended-scalars - ${graphql-java.version} - - - - org.slf4j - slf4j-simple - ${slf4j.version} - test - - - org.assertj - assertj-core - 3.26.3 - test - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - - diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/api/APIQuery.java b/acorn-graphql/src/main/java/com/datasqrl/ai/api/APIQuery.java deleted file mode 100644 index 2fccb89..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/api/APIQuery.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.datasqrl.ai.api; - -/** Defines the API query for a function */ -public interface APIQuery { - - String query(); -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/api/APIQueryExecutor.java b/acorn-graphql/src/main/java/com/datasqrl/ai/api/APIQueryExecutor.java deleted file mode 100644 index 039f037..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/api/APIQueryExecutor.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.datasqrl.ai.api; - -import com.datasqrl.ai.tool.FunctionDefinition; -import com.datasqrl.ai.tool.ValidationResult; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.util.concurrent.CompletableFuture; -import lombok.NonNull; - -/** Interface for querying and writing to an API */ -public interface APIQueryExecutor { - - ObjectMapper getObjectMapper(); - - /** - * Validates that the provided arguments are valid for the given {@link FunctionDefinition}. - * - * @param functionDef - * @param arguments - * @return - */ - ValidationResult validate(@NonNull FunctionDefinition functionDef, JsonNode arguments); - - /** - * Validates whether the provided query is a valid query for this API executor. - * - * @param query the query to validate - * @return the validation result - */ - ValidationResult validate(APIQuery query); - - /** - * Executes the given query with the provided arguments against the API and returns the result as - * a String. - * - * @param query the query to execute - * @param arguments the arguments for the query - * @return The result of the query as a String - * @throws IOException if the connection to the API failed or the query could not be executed - */ - String executeQuery(APIQuery query, JsonNode arguments) throws IOException; - - /** - * Executes an asynchronous request against the API for the given query with arguments. - * - * @param query the query to execute - * @param arguments the arguments for the query - * @return A future for the result - */ - default CompletableFuture executeQueryAsync(APIQuery query, JsonNode arguments) - throws IOException { - return CompletableFuture.supplyAsync( - () -> { - try { - return executeQuery(query, arguments); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/api/GraphQLQuery.java b/acorn-graphql/src/main/java/com/datasqrl/ai/api/GraphQLQuery.java deleted file mode 100644 index efef347..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/api/GraphQLQuery.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.datasqrl.ai.api; - -/** - * Default GraphQL query implementation - * - * @param query query string - */ -public record GraphQLQuery(String query) implements APIQuery {} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/chat/APIChatPersistence.java b/acorn-graphql/src/main/java/com/datasqrl/ai/chat/APIChatPersistence.java deleted file mode 100644 index ccef51d..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/chat/APIChatPersistence.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.datasqrl.ai.chat; - -import com.datasqrl.ai.api.APIQuery; -import com.datasqrl.ai.api.APIQueryExecutor; -import com.datasqrl.ai.tool.Context; -import com.datasqrl.ai.tool.FunctionUtil; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.MissingNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import graphql.com.google.common.collect.Iterators; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import lombok.NonNull; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -@Value -@Slf4j -public class APIChatPersistence implements ChatPersistence { - - APIQueryExecutor apiExecutor; - APIQuery saveMessage; - APIQuery getMessages; - Set messageContextKeys; - - /** - * Saves the generic chat message with the configured context asynchronously (i.e. does not block) - * - * @param message chat message to save - * @return A future for this asynchronous operation which returns the result as a string. - */ - public CompletableFuture saveChatMessage( - @NonNull Object message, @NonNull Context context) { - ObjectMapper mapper = apiExecutor.getObjectMapper(); - ObjectNode payload = mapper.valueToTree(message); - // Inline context variables - context.forEach( - (k, v) -> { - if (payload.has(k)) { - log.warn("Context variable overlaps with message field and is ignored: {}", k); - } else { - payload.set(k, mapper.valueToTree(v)); - } - }); - ; - try { - return apiExecutor.executeQueryAsync(saveMessage, payload); - } catch (IOException e) { - log.error("Failed to save chat message: ", e); - return CompletableFuture.failedFuture(e); - } - } - - /** - * Retrieves saved chat messages from the API via the configured function call. If no function - * call for message retrieval is configured, an empty list is returned. - * - *

Uses the configured context to retrieve user or context specific chat messages. - * - * @param context Arbitrary session context that identifies a user or provides contextual - * information. - * @return Saved messages for the provided context - */ - public List getChatMessages( - @NonNull Context context, int limit, @NonNull Class clazz) throws IOException { - ObjectMapper mapper = apiExecutor.getObjectMapper(); - ObjectNode arguments = mapper.createObjectNode(); - arguments.put("limit", limit); - JsonNode variables = - FunctionUtil.addOrOverrideContext(arguments, messageContextKeys, context, mapper); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - - String response = apiExecutor.executeQuery(getMessages, variables); - JsonNode root = mapper.readTree(response); - JsonNode messages = - Optional.ofNullable(Iterators.getOnlyElement(root.path("data").fields(), null)) - .map(Map.Entry::getValue) - .orElse(MissingNode.getInstance()); - - List chatMessages = new ArrayList<>(); - for (JsonNode node : messages) { - ChatMessage chatMessage = mapper.treeToValue(node, clazz); - chatMessages.add(chatMessage); - } - Collections.reverse(chatMessages); // newest should be last - return chatMessages; - } -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/chat/ChatPersistence.java b/acorn-graphql/src/main/java/com/datasqrl/ai/chat/ChatPersistence.java deleted file mode 100644 index 0bf60db..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/chat/ChatPersistence.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.datasqrl.ai.chat; - -import com.datasqrl.ai.tool.Context; -import java.io.IOException; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import lombok.NonNull; - -/** Interface for saving and retrieving messages against a GraphQL API */ -public interface ChatPersistence { - - ChatPersistence NONE = - new ChatPersistence() { - - @Override - public CompletableFuture saveChatMessage( - @NonNull Object message, @NonNull Context context) { - return CompletableFuture.completedFuture("disabled"); - } - - @Override - public List getChatMessages( - @NonNull Context context, int limit, @NonNull Class clazz) - throws IOException { - return List.of(); - } - - @Override - public Set getMessageContextKeys() { - return Set.of(); - } - }; - - /** - * Saves the given message to the API - * - * @param message the generic message object that is serialized with Jackson - * @param context the sensitive context of the message. The context can contains user, session, - * and other information - * @return - */ - public CompletableFuture saveChatMessage( - @NonNull Object message, @NonNull Context context); - - /** - * Retrieves messages from the API for a given context. - * - * @param context The context to retrieve messages in. Contains user and session information. - * @param limit The maximum number of messages to retrieve - * @param clazz The type of message to return - * @return - * @param - * @throws IOException - */ - public List getChatMessages( - @NonNull Context context, int limit, @NonNull Class clazz) throws IOException; - - Set getMessageContextKeys(); -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/converter/APIFunctionFactory.java b/acorn-graphql/src/main/java/com/datasqrl/ai/converter/APIFunctionFactory.java deleted file mode 100644 index d18c06a..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/converter/APIFunctionFactory.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.datasqrl.ai.converter; - -import com.datasqrl.ai.api.APIQuery; -import com.datasqrl.ai.tool.APIFunction; -import com.datasqrl.ai.tool.FunctionDefinition; - -/** Factory for {@link APIFunction} given a {@link FunctionDefinition} and {@link APIQuery} */ -@FunctionalInterface -public interface APIFunctionFactory { - - APIFunction create(FunctionDefinition function, APIQuery query); -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/converter/GraphQLSchemaConverter.java b/acorn-graphql/src/main/java/com/datasqrl/ai/converter/GraphQLSchemaConverter.java deleted file mode 100644 index 4accabe..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/converter/GraphQLSchemaConverter.java +++ /dev/null @@ -1,556 +0,0 @@ -package com.datasqrl.ai.converter; - -import static graphql.Scalars.GraphQLString; - -import com.datasqrl.ai.api.APIQuery; -import com.datasqrl.ai.api.GraphQLQuery; -import com.datasqrl.ai.tool.APIFunction; -import com.datasqrl.ai.tool.FunctionDefinition; -import com.datasqrl.ai.tool.FunctionDefinition.Argument; -import com.datasqrl.ai.tool.FunctionDefinition.Parameters; -import com.datasqrl.ai.util.ErrorHandling; -import graphql.language.Comment; -import graphql.language.Definition; -import graphql.language.Document; -import graphql.language.ListType; -import graphql.language.NonNullType; -import graphql.language.OperationDefinition; -import graphql.language.OperationDefinition.Operation; -import graphql.language.SourceLocation; -import graphql.language.Type; -import graphql.language.TypeName; -import graphql.language.VariableDefinition; -import graphql.parser.Parser; -import graphql.scalars.ExtendedScalars; -import graphql.schema.GraphQLArgument; -import graphql.schema.GraphQLEnumType; -import graphql.schema.GraphQLEnumValueDefinition; -import graphql.schema.GraphQLFieldDefinition; -import graphql.schema.GraphQLInputObjectField; -import graphql.schema.GraphQLInputObjectType; -import graphql.schema.GraphQLInputType; -import graphql.schema.GraphQLList; -import graphql.schema.GraphQLNonNull; -import graphql.schema.GraphQLObjectType; -import graphql.schema.GraphQLOutputType; -import graphql.schema.GraphQLScalarType; -import graphql.schema.GraphQLSchema; -import graphql.schema.GraphQLType; -import graphql.schema.idl.RuntimeWiring; -import graphql.schema.idl.SchemaGenerator; -import graphql.schema.idl.SchemaParser; -import graphql.schema.idl.SchemaPrinter; -import graphql.schema.idl.TypeDefinitionRegistry; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import lombok.Value; -import lombok.extern.slf4j.Slf4j; - -/** - * Converts a given GraphQL Schema to a tools configuration for the function backend. It extracts - * all queries and mutations and converts them into {@link com.datasqrl.ai.tool.APIFunction}. - */ -@Value -@Slf4j -public class GraphQLSchemaConverter { - - APIFunctionFactory functionFactory; - GraphQLSchemaConverterConfig config; - GraphQLSchema schema; - - public GraphQLSchemaConverter(String schemaString, APIFunctionFactory functionFactory) { - this(schemaString, GraphQLSchemaConverterConfig.DEFAULT, functionFactory); - } - - public GraphQLSchemaConverter( - String schemaString, - GraphQLSchemaConverterConfig config, - APIFunctionFactory functionFactory) { - this.functionFactory = functionFactory; - this.config = config; - this.schema = getSchema(schemaString); - } - - private static GraphQLSchema getSchema(String schemaString) { - TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(schemaString); - RuntimeWiring.Builder runtimeWiringBuilder = RuntimeWiring.newRuntimeWiring(); - getExtendedScalars().forEach(runtimeWiringBuilder::scalar); - // custom name for bigInteger on sqrl - runtimeWiringBuilder.scalar( - ExtendedScalars.GraphQLBigInteger.transform(builder -> builder.name("GraphQLBigInteger"))); - return new SchemaGenerator().makeExecutableSchema(typeRegistry, runtimeWiringBuilder.build()); - } - - SchemaPrinter schemaPrinter = - new SchemaPrinter(SchemaPrinter.Options.defaultOptions().descriptionsAsHashComments(true)); - - /** - * Converts all operations defined within a given GraphQL operation definition string to an - * equivalent list of API Functions. - * - * @param operationDefinition a string defining GraphQL operations - * @return a list of API Functions equivalent to the provided GraphQL operations - * @throws IllegalArgumentException if operation definition contains no definitions or if an - * unexpected definition type is provided - */ - public List convertOperations(String operationDefinition) { - Parser parser = new Parser(); - Document document = parser.parseDocument(operationDefinition); - ErrorHandling.checkArgument( - !document.getDefinitions().isEmpty(), "Operation definition contains no definitions"); - - List functions = new ArrayList<>(); - Iterator defIter = document.getDefinitions().iterator(); - Definition definition = defIter.next(); - do { - ErrorHandling.checkArgument( - definition instanceof OperationDefinition, - "Expected definition to be an operation, but got: %s", - operationDefinition); - FunctionDefinition fctDef = convertOperationDefinition(((OperationDefinition) definition)); - - SourceLocation startLocation = definition.getSourceLocation(); - SourceLocation endLocation = new SourceLocation(Integer.MAX_VALUE, Integer.MAX_VALUE); - definition = null; - if (defIter.hasNext()) { - definition = defIter.next(); - endLocation = definition.getSourceLocation(); - } - // Get string between source and end location - String queryString = - extractOperation( - operationDefinition, - startLocation.getLine(), - startLocation.getColumn(), - endLocation.getLine(), - endLocation.getColumn()); - APIQuery query = new GraphQLQuery(queryString); - functions.add(functionFactory.create(fctDef, query)); - } while (definition != null); - return functions; - } - - private static String extractOperation( - String text, int startLine, int startColumn, int endLine, int endColumn) { - String[] lines = text.split("\n"); - StringBuilder result = new StringBuilder(); - endLine = Math.min(endLine, lines.length); - for (int i = startLine - 1; i <= endLine - 1; i++) { - String line = lines[i]; - String subLine; - if (i == startLine - 1 && i == endLine - 1) { - subLine = line.substring(startColumn - 1, Math.min(endColumn - 1, line.length())); - } else if (i == startLine - 1) { - subLine = line.substring(startColumn - 1); - } else if (i == endLine - 1) { - subLine = (line.substring(0, Math.min(endColumn - 1, line.length()))); - } else { - subLine = line; - } - int index = subLine.indexOf('#'); - subLine = (index != -1) ? subLine.substring(0, index) : subLine; - if (!subLine.isBlank()) { - result.append(subLine); - result.append("\n"); - } - } - - return result.toString(); - } - - private static String comments2String(List comments) { - return comments.stream().map(Comment::getContent).collect(Collectors.joining(" ")); - } - - /** - * Converts a given GraphQL operation definition into a FunctionDefinition. - * - * @param node the OperationDefinition to be converted - * @return a FunctionDefinition that corresponds to the provided OperationDefinition - */ - public FunctionDefinition convertOperationDefinition(OperationDefinition node) { - Operation op = node.getOperation(); - ErrorHandling.checkArgument( - op == Operation.QUERY || op == Operation.MUTATION, - "Do not support subscriptions: %s", - node.getName()); - String fctComment = comments2String(node.getComments()); - String fctName = node.getName(); - - FunctionDefinition funcDef = initializeFunctionDefinition(fctName, fctComment); - Parameters params = funcDef.getParameters(); - - // Process variable definitions - List variableDefinitions = node.getVariableDefinitions(); - for (VariableDefinition varDef : variableDefinitions) { - String description = comments2String(varDef.getComments()); - String argName = varDef.getName(); - Type type = varDef.getType(); - - boolean required = false; - if (type instanceof NonNullType nonNullType) { - required = true; - type = nonNullType.getType(); - } - Argument argDef = convert(type); - argDef.setDescription(description); - if (required) params.getRequired().add(argName); - params.getProperties().put(argName, argDef); - } - return funcDef; - } - - private record OperationField(Operation op, GraphQLFieldDefinition fieldDefinition) {} - - /** - * Converts the whole GraphQL schema into a list of {@link APIFunction} instances. - * - *

This method will take the schema associated with this converter instance and convert every - * query and mutation in the schema into an equivalent {@link APIFunction}. The {@link - * APIFunction} instances are the ones that can be used by other parts of the system, acting as an - * equivalent representation of the original GraphQL operations. - * - * @return List of {@link APIFunction} instances corresponding to all the queries and mutations in - * the GraphQL schema. - */ - public List convertSchema() { - List functions = new ArrayList<>(); - - List queries = - Optional.ofNullable(schema.getQueryType()) - .map(GraphQLObjectType::getFieldDefinitions) - .orElse(List.of()); - List mutations = - Optional.ofNullable(schema.getMutationType()) - .map(GraphQLObjectType::getFieldDefinitions) - .orElse(List.of()); - Stream.concat( - queries.stream().map(fieldDef -> new OperationField(Operation.QUERY, fieldDef)), - mutations.stream().map(fieldDef -> new OperationField(Operation.MUTATION, fieldDef))) - .flatMap( - input -> { - try { - if (config - .getOperationFilter() - .test(input.op(), input.fieldDefinition().getName())) { - return Stream.of(convert(input.op(), input.fieldDefinition())); - } // else filter out - return Stream.of(); - } catch (Exception e) { - log.error("Error converting query: {}", input.fieldDefinition().getName(), e); - return Stream.of(); - } - }) - .forEach(functions::add); - return functions; - } - - private Argument convert(Type type) { - if (type instanceof NonNullType) { - return convert(((NonNullType) type).getType()); - } - Argument argument = new Argument(); - if (type instanceof ListType) { - argument.setType("array"); - argument.setItems(convert(((ListType) type).getType())); - } else if (type instanceof TypeName) { - String typeName = ((TypeName) type).getName(); - GraphQLType graphQLType = schema.getType(typeName); - if (graphQLType instanceof GraphQLInputType graphQLInputType) { - return GraphQLSchemaConverter.this.convert(graphQLInputType); - } else { - throw new UnsupportedOperationException( - "Unexpected type [" + typeName + "] with class: " + graphQLType); - } - } else throw new UnsupportedOperationException("Unexpected type: " + type); - return argument; - } - - private static List getExtendedScalars() { - List scalars = new ArrayList<>(); - - Field[] fields = ExtendedScalars.class.getFields(); - for (Field field : fields) { - if (Modifier.isPublic(field.getModifiers()) - && Modifier.isStatic(field.getModifiers()) - && GraphQLScalarType.class.isAssignableFrom(field.getType())) { - try { - scalars.add((GraphQLScalarType) field.get(null)); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - } - - return scalars; - } - - private record Context(String opName, String prefix, int numArgs, List path) { - - public Context nested(String fieldName, GraphQLObjectType type, int additionalArgs) { - List nestedPath = new ArrayList<>(path); - nestedPath.add(type); - return new Context( - opName + "." + fieldName, - combineStrings(prefix, fieldName), - numArgs + additionalArgs, - nestedPath); - } - } - - private static FunctionDefinition initializeFunctionDefinition(String name, String description) { - FunctionDefinition funcDef = new FunctionDefinition(); - Parameters params = new Parameters(); - params.setType("object"); - params.setProperties(new HashMap<>()); - params.setRequired(new ArrayList<>()); - funcDef.setName(name); - funcDef.setDescription(description); - funcDef.setParameters(params); - return funcDef; - } - - private APIFunction convert(Operation operationType, GraphQLFieldDefinition fieldDef) { - FunctionDefinition funcDef = - initializeFunctionDefinition(fieldDef.getName(), fieldDef.getDescription()); - Parameters params = funcDef.getParameters(); - String opName = operationType.name().toLowerCase() + "." + fieldDef.getName(); - StringBuilder queryHeader = - new StringBuilder(operationType.name().toLowerCase()) - .append(" ") - .append(fieldDef.getName()) - .append("("); - StringBuilder queryBody = new StringBuilder(); - - visit(fieldDef, queryBody, queryHeader, params, new Context(opName, "", 0, List.of())); - - queryHeader.append(") {\n").append(queryBody).append("\n}"); - APIQuery apiQuery = new GraphQLQuery(queryHeader.toString()); - return functionFactory.create(funcDef, apiQuery); - } - - private static String combineStrings(String prefix, String suffix) { - return prefix + (prefix.isBlank() ? "" : "_") + suffix; - } - - private record UnwrappedType(GraphQLInputType type, boolean required) {} - - private UnwrappedType convertRequired(GraphQLInputType type) { - boolean required = false; - if (type instanceof GraphQLNonNull) { - required = true; - type = (GraphQLInputType) ((GraphQLNonNull) type).getWrappedType(); - } - return new UnwrappedType(type, required); - } - - private Argument convert(GraphQLInputType graphQLInputType) { - Argument argument = new Argument(); - if (graphQLInputType instanceof GraphQLScalarType) { - argument.setType(convertScalarTypeToJsonType((GraphQLScalarType) graphQLInputType)); - } else if (graphQLInputType instanceof GraphQLEnumType enumType) { - argument.setType("string"); - argument.setEnumValues( - enumType.getValues().stream() - .map(GraphQLEnumValueDefinition::getName) - .collect(Collectors.toSet())); - } else if (graphQLInputType instanceof GraphQLList) { - argument.setType("array"); - argument.setItems( - convert( - convertRequired((GraphQLInputType) ((GraphQLList) graphQLInputType).getWrappedType()) - .type())); - } else { - throw new UnsupportedOperationException("Unsupported type: " + graphQLInputType); - } - return argument; - } - - public String convertScalarTypeToJsonType(GraphQLScalarType scalarType) { - return switch (scalarType.getName()) { - case "Int" -> "integer"; - case "Float" -> "number"; - case "String" -> "string"; - case "Boolean" -> "boolean"; - case "ID" -> "string"; // Typically treated as a string in JSON Schema - default -> "string"; // We assume that type can be cast from string. - }; - } - - public boolean visit( - GraphQLFieldDefinition fieldDef, - StringBuilder queryBody, - StringBuilder queryHeader, - Parameters params, - Context ctx) { - GraphQLOutputType type = unwrapType(fieldDef.getType()); - if (type instanceof GraphQLObjectType) { - GraphQLObjectType objectType = (GraphQLObjectType) type; - // Don't recurse in a cycle or if depth limit is exceeded - if (ctx.path().contains(objectType)) { - log.info("Detected cycle on operation `{}`. Aborting traversal.", ctx.opName()); - return false; - } else if (ctx.path().size() + 1 > config.getMaxDepth()) { - log.info("Aborting traversal because depth limit exceeded on operation `{}`", ctx.opName()); - return false; - } - } - - queryBody.append(fieldDef.getName()); - int numArgs = 0; - if (!fieldDef.getArguments().isEmpty()) { - queryBody.append("("); - for (Iterator args = fieldDef.getArguments().iterator(); args.hasNext(); ) { - GraphQLArgument arg = args.next(); - - UnwrappedType unwrappedType = convertRequired(arg.getType()); - if (unwrappedType.type() instanceof GraphQLInputObjectType inputType) { - queryBody.append(arg.getName()).append(": { "); - for (Iterator nestedFields = - inputType.getFieldDefinitions().iterator(); - nestedFields.hasNext(); ) { - GraphQLInputObjectField nestedField = nestedFields.next(); - String argName = combineStrings(ctx.prefix(), nestedField.getName()); - unwrappedType = convertRequired(nestedField.getType()); - argName = - processField( - queryBody, - queryHeader, - params, - ctx, - unwrappedType, - argName, - nestedField.getName(), - nestedField.getDescription()); - String typeString = printFieldType(nestedField); - queryHeader.append(argName).append(": ").append(typeString); - numArgs++; - if (nestedFields.hasNext()) { - queryBody.append(", "); - } - } - queryBody.append(" }"); - } else { - String argName = combineStrings(ctx.prefix(), arg.getName()); - argName = - processField( - queryBody, - queryHeader, - params, - ctx, - unwrappedType, - argName, - arg.getName(), - arg.getDescription()); - String typeString = printArgumentType(arg); - queryHeader.append(argName).append(": ").append(typeString); - numArgs++; - } - - if (args.hasNext()) { - queryBody.append(", "); - } - } - queryBody.append(")"); - } - if (type instanceof GraphQLObjectType) { - GraphQLObjectType objectType = (GraphQLObjectType) type; - List nestedPath = new ArrayList<>(ctx.path()); - nestedPath.add(objectType); - queryBody.append(" {\n"); - boolean atLeastOneField = false; - for (GraphQLFieldDefinition nestedField : objectType.getFieldDefinitions()) { - boolean success = - visit( - nestedField, - queryBody, - queryHeader, - params, - ctx.nested(nestedField.getName(), objectType, numArgs)); - atLeastOneField |= success; - } - ErrorHandling.checkArgument( - atLeastOneField, "Expected at least on field on path: {}", ctx.opName()); - queryBody.append("}"); - } - queryBody.append("\n"); - return true; - } - - private String processField( - StringBuilder queryBody, - StringBuilder queryHeader, - Parameters params, - Context ctx, - UnwrappedType unwrappedType, - String argName, - String originalName, - String description) { - Argument argDef = convert(unwrappedType.type()); - argDef.setDescription(description); - if (unwrappedType.required()) params.getRequired().add(argName); - params.getProperties().put(argName, argDef); - argName = "$" + argName; - queryBody.append(originalName).append(": ").append(argName); - return argName; - } - - private String printFieldType(GraphQLInputObjectField field) { - GraphQLInputObjectType type = - GraphQLInputObjectType.newInputObject().name("DummyType").field(field).build(); - // Print argument as part of a dummy field in a dummy schema - String output = schemaPrinter.print(type); - return extractTypeFromDummy(output, field.getName()); - } - - private String printArgumentType(GraphQLArgument argument) { - GraphQLArgument argumentWithoutDescription = - argument.transform(builder -> builder.description(null)); - GraphQLObjectType type = - GraphQLObjectType.newObject() - .name("DummyType") - .field( - field -> - field - .name("dummyField") - .type(GraphQLString) - .argument(argumentWithoutDescription)) - .build(); - // Print argument as part of a dummy field in a dummy schema - String output = schemaPrinter.print(type); - return extractTypeFromDummy(output, argument.getName()); - } - - private String extractTypeFromDummy(String output, String fieldName) { - // Remove comments - output = - Arrays.stream(output.split("\n")) - .filter(line -> !line.trim().startsWith("#")) - .collect(Collectors.joining("\n")); - Pattern pattern = Pattern.compile(fieldName + "\\s*:\\s*([^)}]+)"); - // Print argument as part of a dummy field in a dummy schema - Matcher matcher = pattern.matcher(output); - ErrorHandling.checkArgument(matcher.find(), "Could not find type in: %s", output); - return matcher.group(1).trim(); - } - - private static GraphQLOutputType unwrapType(GraphQLOutputType type) { - if (type instanceof GraphQLList) { - return unwrapType((GraphQLOutputType) ((GraphQLList) type).getWrappedType()); - } else if (type instanceof GraphQLNonNull) { - return unwrapType((GraphQLOutputType) ((GraphQLNonNull) type).getWrappedType()); - } else { - return type; - } - } -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/converter/GraphQLSchemaConverterConfig.java b/acorn-graphql/src/main/java/com/datasqrl/ai/converter/GraphQLSchemaConverterConfig.java deleted file mode 100644 index 0f7c487..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/converter/GraphQLSchemaConverterConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.datasqrl.ai.converter; - -import graphql.language.OperationDefinition.Operation; -import java.util.Arrays; -import java.util.function.BiPredicate; -import lombok.Builder; -import lombok.Value; - -/** Configuration class for {@link GraphQLSchemaConverter}. */ -@Value -@Builder -public class GraphQLSchemaConverterConfig { - - public static final GraphQLSchemaConverterConfig DEFAULT = - GraphQLSchemaConverterConfig.builder().build(); - - /** Filter for selecting which operations to convert */ - @Builder.Default BiPredicate operationFilter = (op, name) -> true; - - /** The maximum depth of conversion for operations that have nested types */ - @Builder.Default int maxDepth = 3; - - /** - * Returns an operations filter that filters out all operations which start with the given list of - * prefixes. - * - * @param prefixes - * @return - */ - public static BiPredicate ignorePrefix(String... prefixes) { - final String[] prefixesLower = - Arrays.stream(prefixes).map(String::trim).map(String::toLowerCase).toArray(String[]::new); - return (op, name) -> - Arrays.stream(prefixesLower) - .anyMatch(prefixLower -> !name.trim().toLowerCase().startsWith(prefixLower)); - } -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/converter/StandardAPIFunctionFactory.java b/acorn-graphql/src/main/java/com/datasqrl/ai/converter/StandardAPIFunctionFactory.java deleted file mode 100644 index 6c6d24f..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/converter/StandardAPIFunctionFactory.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.datasqrl.ai.converter; - -import com.datasqrl.ai.api.APIQuery; -import com.datasqrl.ai.api.APIQueryExecutor; -import com.datasqrl.ai.tool.APIFunction; -import com.datasqrl.ai.tool.FunctionDefinition; -import java.util.Set; - -/** - * This is the factory class for creating APIFunction instances. It implements the - * APIFunctionFactory interface. The class is implemented using Java Records feature, which is a - * final immutable class by default. Being a factory class, its main responsibility is to generate - * and return instances of APIFunction class. - * - * @param apiExecutor is a APIQueryExecutor type object which executes the APIQuery. - * @param contextKeys is a set of Strings which are considered as keys in the context of this - * APIFunctionFactory. - *

Note: Any change in the fields of this class or method definitions will affect the objects - * created by this factory. - */ -public record StandardAPIFunctionFactory(APIQueryExecutor apiExecutor, Set contextKeys) - implements APIFunctionFactory { - - @Override - public APIFunction create(FunctionDefinition function, APIQuery query) { - return new APIFunction(function, contextKeys, query, apiExecutor); - } -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/tool/APIFunction.java b/acorn-graphql/src/main/java/com/datasqrl/ai/tool/APIFunction.java deleted file mode 100644 index 8a52e5a..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/tool/APIFunction.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.datasqrl.ai.tool; - -import com.datasqrl.ai.api.APIQuery; -import com.datasqrl.ai.api.APIQueryExecutor; -import com.datasqrl.ai.tool.FunctionDefinition.Parameters; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; -import java.io.IOException; -import java.util.Map; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import lombok.NonNull; -import lombok.Value; - -/** - * Represents an API function or tool that. It contains the {@link FunctionDefinition} that is - * passed to the LLM as a tool and the {@link APIQuery} that is executed via the {@link - * APIQueryExecutor} to invoke this function/tool. - */ -@Value -public class APIFunction { - - public static final String INVALID_CALL_MSG = - "It looks like you tried to call function `%s`, " - + "but this has failed with the following error: %s" - + ". Please retry to call the function again. Send ONLY the JSON as a response."; - - FunctionDefinition function; - Set contextKeys; - APIQuery apiQuery; - @JsonIgnore APIQueryExecutor apiExecutor; - - public APIFunction( - @NonNull FunctionDefinition function, - @NonNull Set contextKeys, - @NonNull APIQuery apiQuery, - @NonNull APIQueryExecutor apiExecutor) { - this.function = function; - this.contextKeys = contextKeys; - this.apiQuery = apiQuery; - this.apiExecutor = apiExecutor; - ValidationResult result = apiExecutor.validate(apiQuery); - if (!result.isValid()) { - throw new IllegalArgumentException( - "Function [" - + function.getName() - + "] invalid for API [" - + apiExecutor - + "]: " - + result.errorMessage()); - } - } - - @JsonIgnore - public String getName() { - return function.getName(); - } - - /** - * Removes the context keys from the {@link FunctionDefinition} to be passed to the LLM as - * tooling. - * - * @return LLM tool - */ - @JsonIgnore - public FunctionDefinition getModelFunction() { - Predicate fieldFilter = getFieldFilter(contextKeys); - Parameters newParams = - Parameters.builder() - .type(function.getParameters().getType()) - .required(function.getParameters().getRequired().stream().filter(fieldFilter).toList()) - .properties( - function.getParameters().getProperties().entrySet().stream() - .filter(e -> fieldFilter.test(e.getKey())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))) - .build(); - - return FunctionDefinition.builder() - .name(function.getName()) - .description(function.getDescription()) - .parameters(newParams) - .build(); - } - - private static Predicate getFieldFilter(Set fieldList) { - Set contextFilter = - fieldList.stream().map(String::toLowerCase).collect(Collectors.toUnmodifiableSet()); - return field -> !contextFilter.contains(field.toLowerCase()); - } - - /** - * Validate the arguments for this function/tool. - * - * @param arguments - * @return - */ - public ValidationResult validate(JsonNode arguments) { - return apiExecutor.validate(getModelFunction(), arguments); - } - - /** - * Executes the given function with the provided arguments and context. - * - * @param arguments Arguments to the function - * @param context session context that is added to the arguments - * @return The result as string - * @throws IOException - */ - public String execute(JsonNode arguments, @NonNull Context context) throws IOException { - JsonNode variables = - FunctionUtil.addOrOverrideContext( - arguments, contextKeys, context, apiExecutor.getObjectMapper()); - return apiExecutor.executeQuery(apiQuery, variables); - } - - public String validateAndExecute(JsonNode arguments, @NonNull Context context) - throws IOException { - ValidationResult result = validate(arguments); - if (result.isValid()) { - return execute(arguments, context); - } else { - return String.format(INVALID_CALL_MSG, getFunction().getName(), result.errorMessage()); - } - } - - public String validateAndExecute(String arguments, @NonNull Context context) throws IOException { - try { - return validateAndExecute(apiExecutor.getObjectMapper().readTree(arguments), context); - } catch (JsonMappingException ex) { - return String.format( - INVALID_CALL_MSG, getFunction().getName(), "Malformed JSON:" + ex.getMessage()); - } - } -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/tool/Context.java b/acorn-graphql/src/main/java/com/datasqrl/ai/tool/Context.java deleted file mode 100644 index a3d9764..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/tool/Context.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.datasqrl.ai.tool; - -import java.util.Collections; -import java.util.Map; -import java.util.function.BiConsumer; - -/** - * The {@link Context} class captures the context of an agent interaction. It has a request id that - * is unique for each interaction and an invocation counter for the number of times the LLM is - * invoked in the course of producing a request response. - * - *

Additional key-value pairs can be provided to securely pass information to the function calls - * outside the LLM call stack. - * - *

The request id and secure information are static for the duration of an interaction. The - * counter is incremented for each time the LLM is invoked. - */ -public interface Context { - - static Context EMPTY = - new Context() { - @Override - public Map asMap() { - return Collections.EMPTY_MAP; - } - }; - - default Object get(String key) { - return asMap().get(key); - } - - default void forEach(BiConsumer action) { - asMap().forEach(action); - } - - Map asMap(); - - default void nextInvocation() { - // Do nothing - } - - static Context of() { - return of(Collections.emptyMap()); - } - - static Context of(Map secure) { - // Invocations are incremented before a model is called, hence we start with -1 (to indicate - // model has not yet been called) so the first invocation is 0. - return new ContextImpl(secure); - } -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/tool/ContextImpl.java b/acorn-graphql/src/main/java/com/datasqrl/ai/tool/ContextImpl.java deleted file mode 100644 index 40ed9e3..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/tool/ContextImpl.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.datasqrl.ai.tool; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.function.BiConsumer; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * The {@link ContextImpl} class captures the context of an agent interaction. It has a request id - * that is unique for each interaction and an invocation counter for the number of times the LLM is - * invoked in the course of producing a request response. - * - *

Additional key-value pairs can be provided to securely pass information to the function calls - * outside the LLM call stack. - * - *

The request id and secure information are static for the duration of an interaction. The - * counter is incremented for each time the LLM is invoked. - */ -@AllArgsConstructor -@Getter -public class ContextImpl implements Context { - - private final Map secure; - - public Object get(String key) { - return secure.get(key); - } - - public void forEach(BiConsumer action) { - Objects.requireNonNull(action); - secure.forEach(action); - } - - public Map asMap() { - Map result = new HashMap<>(secure.size() + 2); - forEach(result::put); - return result; - } -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/tool/FunctionDefinition.java b/acorn-graphql/src/main/java/com/datasqrl/ai/tool/FunctionDefinition.java deleted file mode 100644 index 9905c9b..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/tool/FunctionDefinition.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.datasqrl.ai.tool; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; -import java.util.Map; -import java.util.Set; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * Definition of a chat function that can be invoked by the language model. - * - *

This is essentially a java definition of the json structure OpenAI and most LLMs use to - * represent functions/tools. - * - *

This should be updated whenever the model representation of a function changes. It should not - * contain any extra functionality - those should be added to the respective wrapper classes. - */ -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class FunctionDefinition { - - private String name; - private String description; - private Parameters parameters; - - @Data - @Builder - @AllArgsConstructor - @NoArgsConstructor - public static class Parameters { - - private String type; - private Map properties = Map.of(); - private List required = List.of(); - } - - @Data - public static class Argument { - - private String type; - private String description; - private Argument items; - - @JsonProperty("enum") - private Set enumValues; - } -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/tool/FunctionUtil.java b/acorn-graphql/src/main/java/com/datasqrl/ai/tool/FunctionUtil.java deleted file mode 100644 index aa9d7dc..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/tool/FunctionUtil.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.datasqrl.ai.tool; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.io.IOException; -import java.util.List; -import java.util.Set; -import lombok.NonNull; - -public class FunctionUtil { - - public static String toJsonString(List tools) throws IOException { - ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapper.valueToTree(tools)); - } - - /** - * Adds/overwrites the context fields on the message with the provided context. - * - * @param arguments - * @param contextKeys - * @param context - * @param mapper - * @return - */ - public static JsonNode addOrOverrideContext( - JsonNode arguments, - @NonNull Set contextKeys, - @NonNull Context context, - @NonNull ObjectMapper mapper) { - // Create a copy of the original JsonNode to add context - ObjectNode copyJsonNode; - if (arguments == null || arguments.isEmpty()) { - copyJsonNode = mapper.createObjectNode(); - } else { - copyJsonNode = arguments.deepCopy(); - } - // Add context - for (String contextKey : contextKeys) { - Object value = context.get(contextKey); - if (value == null) { - throw new IllegalArgumentException("Missing context field: " + contextKey); - } - copyJsonNode.putPOJO(contextKey, value); - } - return copyJsonNode; - } -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/tool/ValidationResult.java b/acorn-graphql/src/main/java/com/datasqrl/ai/tool/ValidationResult.java deleted file mode 100644 index 1402cb2..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/tool/ValidationResult.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.datasqrl.ai.tool; - -import lombok.NonNull; - -/** - * The result of a function argument evaluation. - * - * @param errorType - * @param errorMessage - */ -public record ValidationResult(@NonNull ErrorType errorType, String errorMessage) { - - public static final ValidationResult VALID = new ValidationResult(ErrorType.NONE, null); - - public boolean isValid() { - return errorType == ErrorType.NONE; - } - - public enum ErrorType { - NONE, - NOT_FOUND, - INVALID_JSON, - INVALID_ARGUMENT - } -} diff --git a/acorn-graphql/src/main/java/com/datasqrl/ai/util/ErrorHandling.java b/acorn-graphql/src/main/java/com/datasqrl/ai/util/ErrorHandling.java deleted file mode 100644 index b75b335..0000000 --- a/acorn-graphql/src/main/java/com/datasqrl/ai/util/ErrorHandling.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.datasqrl.ai.util; - -/** Utility methods for error handling */ -public class ErrorHandling { - - public static void checkArgument(boolean condition, String message, Object... args) { - if (!condition) { - throw new IllegalArgumentException(String.format(message, args)); - } - } - - public static void checkArgument(boolean condition) { - checkArgument(condition, "Unexpected arguments in method invocation"); - } -} diff --git a/acorn-graphql/src/test/java/com/datasqrl/ai/TestUtil.java b/acorn-graphql/src/test/java/com/datasqrl/ai/TestUtil.java deleted file mode 100644 index 9c3cda2..0000000 --- a/acorn-graphql/src/test/java/com/datasqrl/ai/TestUtil.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.datasqrl.ai; - -import static java.nio.file.StandardOpenOption.CREATE; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; - -import com.datasqrl.ai.util.ErrorHandling; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import lombok.SneakyThrows; - -public class TestUtil { - - public static URL getResourceFile(String path) { - URL url = TestUtil.class.getClassLoader().getResource(path); - ErrorHandling.checkArgument(url != null, "Invalid url: %s", url); - return url; - } - - @SneakyThrows - public static String getResourcesFileAsString(String path) { - Path uriPath = Path.of(getResourceFile(path).toURI()); - return Files.readString(uriPath, StandardCharsets.UTF_8); - } - - @SneakyThrows - public static void snapshotTest(String result, Path pathToExpected) { - if (Files.isRegularFile(pathToExpected)) { - assertThat(pathToExpected).hasContent(result); - } else { - Files.writeString(pathToExpected, result, StandardCharsets.UTF_8, CREATE); - fail("Created snapshot: " + pathToExpected.toAbsolutePath()); - } - } -} diff --git a/acorn-graphql/src/test/java/com/datasqrl/ai/api/MockAPIExecutor.java b/acorn-graphql/src/test/java/com/datasqrl/ai/api/MockAPIExecutor.java deleted file mode 100644 index 798e907..0000000 --- a/acorn-graphql/src/test/java/com/datasqrl/ai/api/MockAPIExecutor.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.datasqrl.ai.api; - -import com.datasqrl.ai.tool.FunctionDefinition; -import com.datasqrl.ai.tool.ValidationResult; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.IOException; -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; -import lombok.NonNull; -import lombok.Value; - -@Value -public class MockAPIExecutor implements APIQueryExecutor { - - ObjectMapper mapper = new ObjectMapper(); - Function queryToResult; - - public static MockAPIExecutor of(@NonNull String uniformResult) { - return new MockAPIExecutor(s -> uniformResult); - } - - @Override - public ObjectMapper getObjectMapper() { - return mapper; - } - - @Override - public ValidationResult validate(@NonNull FunctionDefinition functionDef, JsonNode arguments) { - return ValidationResult.VALID; - } - - @Override - public ValidationResult validate(APIQuery query) { - return ValidationResult.VALID; - } - - @Override - public String executeQuery(APIQuery query, JsonNode arguments) throws IOException { - return queryToResult.apply(query.query()); - } - - @Override - public CompletableFuture executeQueryAsync(APIQuery query, JsonNode arguments) { - return CompletableFuture.completedFuture("mock write"); - } -} diff --git a/acorn-graphql/src/test/java/com/datasqrl/ai/converter/GraphQLSchemaConverterTest.java b/acorn-graphql/src/test/java/com/datasqrl/ai/converter/GraphQLSchemaConverterTest.java deleted file mode 100644 index 09f9dee..0000000 --- a/acorn-graphql/src/test/java/com/datasqrl/ai/converter/GraphQLSchemaConverterTest.java +++ /dev/null @@ -1,156 +0,0 @@ -package com.datasqrl.ai.converter; - -import static com.datasqrl.ai.converter.GraphQLSchemaConverterConfig.ignorePrefix; -import static graphql.Assert.assertFalse; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.datasqrl.ai.TestUtil; -import com.datasqrl.ai.api.APIQueryExecutor; -import com.datasqrl.ai.api.MockAPIExecutor; -import com.datasqrl.ai.tool.APIFunction; -import com.datasqrl.ai.tool.FunctionUtil; -import graphql.parser.Parser; -import graphql.schema.*; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.Set; -import lombok.SneakyThrows; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -public class GraphQLSchemaConverterTest { - - public static APIQueryExecutor apiExecutor = MockAPIExecutor.of("none"); - public static StandardAPIFunctionFactory fctFactory = - new StandardAPIFunctionFactory(apiExecutor, Set.of()); - - @Test - public void testNutshop() { - GraphQLSchemaConverter converter = - new GraphQLSchemaConverter( - TestUtil.getResourcesFileAsString("graphql/nutshop-schema.graphqls"), - GraphQLSchemaConverterConfig.builder() - .operationFilter(ignorePrefix("internal")) - .build(), - new StandardAPIFunctionFactory(apiExecutor, Set.of("customerid"))); - List functions = converter.convertSchema(); - assertEquals(5, functions.size()); - // Test context key handling - APIFunction orders = - functions.stream() - .filter(f -> f.getFunction().getName().equalsIgnoreCase("orders")) - .findFirst() - .get(); - assertTrue(orders.getFunction().getParameters().getProperties().containsKey("customerid")); - assertFalse( - orders.getModelFunction().getParameters().getProperties().containsKey("customerid")); - snapshot(functions, "nutshop"); - } - - @Test - public void testCreditCard() { - List functions = getFunctionsFromPath("graphql/creditcard-rewards.graphqls"); - assertEquals(6, functions.size()); - snapshot(functions, "creditcard-rewards"); - } - - @Test - public void testLawEnforcement() { - List functions = getFunctionsFromPath("graphql/law_enforcement.graphqls"); - assertEquals(7, functions.size()); - snapshot(functions, "law_enforcement"); - } - - @Test - public void testSensors() { - GraphQLSchemaConverter converter = - getConverter(TestUtil.getResourcesFileAsString("graphql/sensors.graphqls")); - List functions = converter.convertSchema(); - assertEquals(5, functions.size()); - List queries = - converter.convertOperations( - TestUtil.getResourcesFileAsString("graphql/sensors-aboveTemp.graphql")); - assertEquals(2, queries.size()); - assertEquals("HighTemps", queries.get(0).getFunction().getName()); - functions.addAll(queries); - snapshot(functions, "sensors"); - } - - public List getFunctionsFromPath(String path) { - return getFunctions(TestUtil.getResourcesFileAsString(path)); - } - - public List getFunctions(String schemaString) { - return getConverter(schemaString).convertSchema(); - } - - public GraphQLSchemaConverter getConverter(String schemaString) { - return new GraphQLSchemaConverter(schemaString, fctFactory); - } - - @Test - @Disabled - public void testSchemaConversion() throws IOException { - String schemaString = - Files.readString( - Path.of( - "../../../datasqrl-examples/finance-credit-card-chatbot/creditcard-analytics.graphqls")); - String result = convertToJsonDefault(getFunctions(schemaString)); - System.out.println(result); - } - - @SneakyThrows - public static String convertToJsonDefault(List functions) { - return FunctionUtil.toJsonString(functions); - } - - public static void snapshot(List functions, String testName) { - for (APIFunction apiFunction : functions) { - // make sure ALL queries have a good syntax - var query = apiFunction.getApiQuery().query(); - assertDoesNotThrow( - () -> { - Parser.parse(query); - }); - } - TestUtil.snapshotTest( - convertToJsonDefault(functions), - Path.of("src", "test", "resources", "snapshot", testName + ".json")); - } - - @Test - void givenComplexFieldDefinition_whenVisiting_thenGenerateValidQuery() { - GraphQLSchemaConverter converter = - new GraphQLSchemaConverter( - TestUtil.getResourcesFileAsString("graphql/rick_morty-schema.graphqls"), - GraphQLSchemaConverterConfig.builder() - .operationFilter(ignorePrefix("internal")) - .build(), - new StandardAPIFunctionFactory(apiExecutor, Set.of())); - - List functions = converter.convertSchema(); - assertEquals(9, functions.size()); - // Test context key handling - APIFunction episodes = - functions.stream() - .filter(f -> f.getFunction().getName().equalsIgnoreCase("episodes")) - .findFirst() - .get(); - assertThat(episodes.getFunction().getParameters().getProperties()) - .containsKeys("name", "episode", "page"); - - var query = episodes.getApiQuery().query(); - assertDoesNotThrow( - () -> { - Parser.parse(query); - }); - assertFalse( - episodes.getModelFunction().getParameters().getProperties().containsKey("customerid")); - snapshot(functions, "rick-morty"); - } -} diff --git a/acorn-graphql/src/test/resources/graphql/creditcard-rewards.graphqls b/acorn-graphql/src/test/resources/graphql/creditcard-rewards.graphqls deleted file mode 100644 index 8a24c95..0000000 --- a/acorn-graphql/src/test/resources/graphql/creditcard-rewards.graphqls +++ /dev/null @@ -1,108 +0,0 @@ -"An RFC-3339 compliant DateTime Scalar" -scalar DateTime - -type Query { - """Returns all the rewards that a customer has earned in the given time period""" - Rewards( - """customerid: Customer identifier""" - customerid: Int!, - """fromTime: RFC-3339 compliant date time scalar. Returns rewards after this time. Use the start of the day only, e.g. 2024-01-19T00:00:00-00:00.""" - fromTime: DateTime!, - """toTime: RFC-3339 compliant date time scalar. Returns rewards up to this time. Use the start of the day only, e.g. 2024-01-19T00:00:00-00:00.""" - toTime: DateTime! - ): [CustomerRewards!] - - """Returns the total awards a customer earned by week starting from the most recent week.""" - RewardsByWeek( - """customerid: Customer identifier""" - customerid: Int!, - """limit: The number of weeks to return starting from most recent to less recent weeks. For example, if limit is 12 it will return the last 12 weeks of total rewards earned.""" - limit: Int = 12, - """offset: The number of weeks to offset. For example, if offset is 4, it will skip the last 4 weeks of rewards earned and return the weeks before that.""" - offset: Int = 0 - ): [RewardsByWeek!] - - """Returns the total amount of rewards the customer has earned to date and the time since when they eared rewards""" - TotalReward( - """customerid: Customer identifier""" - customerid: Int! - ): TotalReward - - """Returns all the potential rewards a customer could have earned in the given time period for the given card type. Use this function to show customers the rewards they would have earned if they had the given card.""" - PotentialRewards( - """customerid: Customer identifier""" - customerid: Int!, - """cardType: The type of card to calculate potential rewards for (i.e. travel, sports, business, or family)""" - cardType: String!, - """fromTime: RFC-3339 compliant date time scalar. Returns rewards after this time. Use the start of the day only, e.g. 2024-01-19T00:00:00-00:00.""" - fromTime: DateTime!, - """toTime: RFC-3339 compliant date time scalar. Returns rewards up to this time. Use the start of the day only, e.g. 2024-01-19T00:00:00-00:00.""" - toTime: DateTime! - ): [PotentialRewards!] - - """Returns the total awards a customer could have earned for a given card type by week starting from the most recent week. Use this function to show the customer what their reward earnings would have looked like, if they had a given card.""" - PotentialRewardsByWeek( - """customerid: Customer identifier""" - customerid: Int!, - """cardType: The type of card to calculate potential rewards for (e.g., travel, sports, business, family)""" - cardType: String!, - """limit: The number of weeks to return starting from most recent to less recent weeks. For example, if limit is 12 it will return the last 12 weeks of total rewards earned.""" - limit: Int = 12, - """offset: The number of weeks to offset. For example, if offset is 4, it will skip the last 4 weeks of rewards earned and return the weeks before that.""" - offset: Int = 0 - ): [PotentialRewardsByWeek!] - - """Returns the total amount of rewards the customer could have earned for each type of credit card the customer does not yet have. Use this function to determine which credit card type to recommend to a customer.""" - TotalPotentialReward( - """customerid: Customer identifier""" - customerid: Int! - ): [TotalPotentialReward!] - -} - -type CustomerRewards { - transactionId: Float! - customerid: Int! - cardNo: Float! - cardType: String! - time: DateTime! - amount: Float! - reward: Float! - merchantName: String! -} - -type RewardsByWeek { - customerid: Int! - timeWeek: DateTime! - total_reward: Float! -} - -type TotalReward { - customerid: Int! - total_reward: Float! - since_time: DateTime! -} - -type PotentialRewards { - transactionId: Float! - customerid: Int! - rewardCardType: String! - time: DateTime! - amount: Float! - reward: Float! - merchantName: String! -} - -type PotentialRewardsByWeek { - customerid: Int! - cardType: String! - timeWeek: DateTime! - total_reward: Float! -} - -type TotalPotentialReward { - customerid: Int! - cardType: String! - total_reward: Float! - since_time: DateTime! -} \ No newline at end of file diff --git a/acorn-graphql/src/test/resources/graphql/law_enforcement.graphqls b/acorn-graphql/src/test/resources/graphql/law_enforcement.graphqls deleted file mode 100644 index 7a8d5cf..0000000 --- a/acorn-graphql/src/test/resources/graphql/law_enforcement.graphqls +++ /dev/null @@ -1,234 +0,0 @@ -"An RFC-3339 compliant DateTime Scalar" -scalar DateTime - - -type Query { - """ - Fetches Bolo details with specified vehicle characteristics. - """ - BoloDetails( - """ - The make of the vehicle (e.g., Ford, Toyota). - """ - make: String!, - - """ - The model of the vehicle. - """ - model: String, - - """ - The maximum number of results to return. Defaults to 10. - """ - limit: Int = 10, - - """ - The number of results to skip before starting to return results. Defaults to 0. - """ - offset: Int = 0 - ): [BoloDetails!] - - """ - Retrieves driver information using their license number. - """ - Driver( - """ - The license number of the driver. - """ - license_number: String!, - - """ - The maximum number of results to return. Defaults to 10. - """ - limit: Int = 10, - - """ - The number of results to skip before starting to return results. Defaults to 0. - """ - offset: Int = 0 - ): [Driver!] - - """ - Retrieves vehicle details using the registration number. - """ - Vehicle( - """ - The registration number of the vehicle, i.e. the license plate number. - """ - registration_number: String!, - - """ - The maximum number of results to return. Defaults to 10. - """ - limit: Int = 10, - - """ - The number of results to skip before starting to return results. Defaults to 0. - """ - offset: Int = 0 - ): [Vehicle!] - - """ - Fetches statistics on warrants filtered by the type of crime. - """ - WarrantsByCrime( - """ - The type of crime. - """ - crime: String, - - limit: Int = 100, - offset: Int = 0 - ): [WarrantsByCrime!] - - """ - Fetches statistics on warrants issued by state filtered by their status. - """ - WarrantsByState( - """ - The status of the warrant (one of: active, urgent, closed, suspended). - """ - status: String, - - limit: Int = 100, - - offset: Int = 0 - ): [WarrantsByState!] - - """ - Fetches Bolo (be on the lookout) statistics by week filtered by state. It returns the data ordered - by week starting from the most recent and going backwards from there. - """ - BolosByWeekState( - """ - The state for which Bolos are to be fetched abbreviated to two uppercase letters (e.g. WA, CA). - """ - state: String, - - """ - The maximum number of results to return. Defaults to 100. Set to the number of past weeks to return - multiplied by the number of states. - """ - limit: Int = 100, - - """ - The number of results to skip before starting to return results. Defaults to 0. - """ - offset: Int = 0 - ): [BolosByWeekState!] -} - -type BoloDetails { - bolo_id: String! - vehicle_id: String! - issue_date: DateTime! - status: String! - last_updated: DateTime! - make: String! - model: String! - year: Float! - registration_state: String! - registration_number: String! - license_state: String! - driver_id: String! - -} - -type BolosByWeekState { - week: DateTime! - state: String! - num_bolos: Float! -} - -type Driver { - driver_id: String! - first_name: String! - last_name: String! - license_number: String! - license_state: String! - date_of_birth: String! - license_status: String! - license_expiry_date: DateTime! - last_updated: DateTime! - bolos(limit: Int = 10, offset: Int = 0): [BoloDetails!] - vehicles(limit: Int = 10, offset: Int = 0): [Vehicle!] - warrants(limit: Int = 10, offset: Int = 0): [Warrant!] -} - -type Vehicle { - vehicle_id: String! - registration_number: String! - registration_state: String! - registration_expiry: DateTime! - make: String! - model: String! - year: Float! - owner_driver_id: String! - last_updated: DateTime! - bolos(limit: Int = 10, offset: Int = 0): [BoloDetails!] - tracking(limit: Int = 10, offset: Int = 0): [Tracking!] -} - -type Warrant { - warrant_id: String! - person_id: String! - warrant_status: String! - crime_description: String! - state_of_issuance: String! - issue_date: DateTime! - last_updated: DateTime! -} - -type WarrantsByCrime { - crime: String! - num_warrants: Float! -} - -type WarrantsByState { - state: String! - status: String! - num_warrants: Float! -} - - -type Tracking { - latitude: Float! - longitude: Float! - event_time: DateTime! -} - -type Mutation { - Tracking(encounter: TrackingInput!): TrackingCreated -} - -input TrackingInput { - plate: String! - latitude: Float! - longitude: Float! -} - -type TrackingCreated { - _uuid: String! - plate: String! -} - -type Subscription { - TrackingAlert: TrackingAlert -} - -type TrackingAlert { - bolo_id: String! - vehicle_id: String! - issue_date: DateTime! - status: String! - last_updated: DateTime! - make: String! - model: String! - year: Float! - registration_state: String! - registration_number: String! - license_state: String! - driver_id: String! - latitude: Float! - longitude: Float! -} \ No newline at end of file diff --git a/acorn-graphql/src/test/resources/graphql/nutshop-schema.graphqls b/acorn-graphql/src/test/resources/graphql/nutshop-schema.graphqls deleted file mode 100644 index dd7d542..0000000 --- a/acorn-graphql/src/test/resources/graphql/nutshop-schema.graphqls +++ /dev/null @@ -1,136 +0,0 @@ -"""An RFC-3339 compliant DateTime Scalar""" -scalar DateTime - -type Query { - """ Retrieve weekly spending data for a specified customer.""" - SpendingByWeek( - """ - customerid: The unique identifier for the customer. - """ - customerid: Int!, - """ The maximum number of results to return (default: 10).""" - limit: Int = 10, - """ The number of results to skip before starting to collect the result set (default: 0).""" - offset: Int = 0 - ): [SpendingByWeek!] - - """ Retrieve product information with optional filtering by product ID.""" - Products( - """ The unique identifier for the product (optional).""" - id: Int, - """ The maximum number of results to return (default: 10).""" - limit: Int = 10, - """ The number of results to skip before starting to collect the result set (default: 0).""" - offset: Int = 0 - ): [Products!] - - """ Retrieve order information for a specified customer.""" - Orders( - """ The unique identifier for the customer.""" - customerid: Int!, - """ The maximum number of results to return (default: 10).""" - limit: Int = 10, - """ The number of results to skip before starting to collect the result set (default: 0).""" - offset: Int = 0 - ): [Orders!] - - """Retrieves orders for a given customer within the specified time range""" - OrdersByTimeRange( - """The unique identifier for the customer""" - customerid: Int!, - """Returns orders where the order timestamp is larger than this RFC-3339 compliant fromTime timestamp""" - fromTime: DateTime!, - """Returns orders where the order timestamp is smaller than this RFC-3339 compliant toTime timestamp""" - toTime: DateTime! - ): [Orders!] - - """ Suggest products for a customer to order again based on their previous orders.""" - OrderAgain( - # The unique identifier for the customer. - customerid: Int!, - # The maximum number of results to return (default: 10). - limit: Int = 10, - # The number of results to skip before starting to collect the result set (default: 0). - offset: Int = 0 - ): [OrderAgain!] - - InternalGetChatMessages( - customerid: Int!, - limit: Int = 10, - offset: Int = 0 - ): [CustomerChatMessage!] -} - -type SpendingByWeek { - week: String! - total_spend: Float! - total_savings: Float! -} - -type OrderAgain { - product: Products! - num: Int! - quantity: Int! -} - -type Orders { - id: Int! - customerid: Int! - timestamp: String! - items(limit: Int = 10): [items!] - total: total! -} - -type items { - quantity: Int! - unit_price: Float! - discount0: Float! - total: Float! - product: Products -} - -type total { - price: Float! - discount: Float! -} - -type Products { - id: Int! - name: String! - sizing: String! - weight_in_gram: Float! - type: String! - category: String! - usda_id: Float! - updated: String! - orders(limit: Int = 10): [Orders!] -} - -type CustomerChatMessage { - role: String! - content: String! - name: String - customerid: Int! - timestamp: String! - uuid: String! -} - -type Mutation { - """ Saves a chat message""" - InternalSaveChatMessage(message: ChatMessageInput!): CreatedChatMessage -} - -input ChatMessageInput { - # The role of the message author - role: String! - # The content of the message - content: String! - # The name of the function called (if any) - name: String - # The id of the customer for the chat session - customerid: Int! -} - -type CreatedChatMessage { - _source_time: String! -} \ No newline at end of file diff --git a/acorn-graphql/src/test/resources/graphql/rick_morty-schema.graphqls b/acorn-graphql/src/test/resources/graphql/rick_morty-schema.graphqls deleted file mode 100644 index 1d7da8c..0000000 --- a/acorn-graphql/src/test/resources/graphql/rick_morty-schema.graphqls +++ /dev/null @@ -1,224 +0,0 @@ - type Query { - """ - Get a specific character by ID - """ - character(id: ID!): Character - - """ - Get the list of all characters - """ - characters(page: Int, filter: FilterCharacter): Characters - - """ - Get a list of characters selected by ids - """ - charactersByIds(ids: [ID!]!): [Character] - - """ - Get a specific locations by ID - """ - location(id: ID!): Location - - """ - Get the list of all locations - """ - locations(page: Int, filter: FilterLocation): Locations - - """ - Get a list of locations selected by ids - """ - locationsByIds(ids: [ID!]!): [Location] - - """ - Get a specific episode by ID - """ - episode(id: ID!): Episode - - """ - Get the list of all episodes - """ - episodes(page: Int, filter: FilterEpisode): Episodes - - """ - Get a list of episodes selected by ids - """ - episodesByIds(ids: [ID!]!): [Episode] - } - - type Characters { - info: Info - results: [Character] - } - - type Locations { - info: Info - results: [Location] - } - - type Episodes { - info: Info - results: [Episode] - } - - type Character { - """ - The id of the character. - """ - id: ID - - """ - The name of the character. - """ - name: String - - """ - The status of the character ('Alive', 'Dead' or 'unknown'). - """ - status: String - - """ - The species of the character. - """ - species: String - - """ - The type or subspecies of the character. - """ - type: String - - """ - The gender of the character ('Female', 'Male', 'Genderless' or 'unknown'). - """ - gender: String - - """ - The character's origin location - """ - origin: Location - - """ - The character's last known location - """ - location: Location - - """ - Link to the character's image. - All images are 300x300px and most are medium shots or portraits since they are intended to be used as avatars. - """ - image: String - - """ - Episodes in which this character appeared. - """ - episode: [Episode]! - - """ - Time at which the character was created in the database. - """ - created: String - } - - type Location { - """ - The id of the location. - """ - id: ID - - """ - The name of the location. - """ - name: String - - """ - The type of the location. - """ - type: String - - """ - The dimension in which the location is located. - """ - dimension: String - - """ - List of characters who have been last seen in the location. - """ - residents: [Character]! - - """ - Time at which the location was created in the database. - """ - created: String - } - - type Episode { - """ - The id of the episode. - """ - id: ID - - """ - The name of the episode. - """ - name: String - - """ - The air date of the episode. - """ - air_date: String - - """ - The code of the episode. - """ - episode: String - - """ - List of characters who have been seen in the episode. - """ - characters: [Character]! - - """ - Time at which the episode was created in the database. - """ - created: String - } - - type Info { - """ - The length of the response. - """ - count: Int - - """ - The amount of pages. - """ - pages: Int - - """ - Number of the next page (if it exists) - """ - next: Int - - """ - Number of the previous page (if it exists) - """ - prev: Int - } - - input FilterCharacter { - name: String - status: String - species: String - type: String - gender: String - } - - input FilterLocation { - name: String - type: String - dimension: String - } - - input FilterEpisode { - name: String - episode: String - } diff --git a/acorn-graphql/src/test/resources/graphql/sensors-aboveTemp.graphql b/acorn-graphql/src/test/resources/graphql/sensors-aboveTemp.graphql deleted file mode 100644 index 378dcd3..0000000 --- a/acorn-graphql/src/test/resources/graphql/sensors-aboveTemp.graphql +++ /dev/null @@ -1,19 +0,0 @@ -# Returns all readings with a temperature higher than the provided value -query HighTemps( - # The temperature - $temperature: Float! -) { - ReadingsAboveTemp(temp: $temperature) { - sensorid - temperature - } -} -# high temperature readings -query HighTemps2( - $temp: Float! -) { - ReadingsAboveTemp(temp: $temp) { - sensorid - temperature - } -} \ No newline at end of file diff --git a/acorn-graphql/src/test/resources/graphql/sensors.graphqls b/acorn-graphql/src/test/resources/graphql/sensors.graphqls deleted file mode 100644 index fcc466b..0000000 --- a/acorn-graphql/src/test/resources/graphql/sensors.graphqls +++ /dev/null @@ -1,69 +0,0 @@ -scalar DateTime - -type Query { - """ - Returns the sensor temperature readings for each second by most recent for a given sensor id - """ - SensorReading( - "The id of the sensor" - sensorid: Int!, - "The number of readings (one per second) to return" - limit: Int = 10, - offset: Int = 0 - ): [SensorReading!] - - """ - Returns all sensor temperature readings above the given temperature - """ - ReadingsAboveTemp( - temp: Float!, - limit: Int = 10 - ): [SensorReading!] - - """ - Returns the maximum temperature recorded by each sensor in the last minute - """ - SensorMaxTempLastMinute( - "The id of the sensor. If left empty, returns max temperature for all sensors." - sensorid: Int, - "The number sensors to return max temperature for" - limit: Int = 10, - offset: Int = 0 - ): [SensorMaxTemp!] - - """ - Returns the maximum temperature recorded by each sensor - """ - SensorMaxTemp( - "The id of the sensor. If left empty, returns max temperature for all sensors." - sensorid: Int, - "The number sensors to return max temperature for" - limit: Int = 10, - offset: Int = 0 - ): [SensorMaxTemp!] -} - -type SensorReading { - sensorid: Int! - temperature: Float! - event_time: DateTime! -} - -type SensorMaxTemp { - sensorid: Int! - maxTemp: Float! - last_updated: DateTime! -} - -type Subscription { - HighTemp(sensorid: Int!): SensorReading -} - -type Mutation { - AddReading(metric: ReadingInput!): SensorReading -} - -input ReadingInput { - sensorid: Int! - temperature: Float! -} diff --git a/acorn-graphql/src/test/resources/snapshot/creditcard-rewards.json b/acorn-graphql/src/test/resources/snapshot/creditcard-rewards.json deleted file mode 100644 index f5b43a5..0000000 --- a/acorn-graphql/src/test/resources/snapshot/creditcard-rewards.json +++ /dev/null @@ -1,155 +0,0 @@ -[ { - "function" : { - "name" : "Rewards", - "description" : "Returns all the rewards that a customer has earned in the given time period", - "parameters" : { - "type" : "object", - "properties" : { - "customerid" : { - "type" : "integer", - "description" : "customerid: Customer identifier" - }, - "fromTime" : { - "type" : "string", - "description" : "fromTime: RFC-3339 compliant date time scalar. Returns rewards after this time. Use the start of the day only, e.g. 2024-01-19T00:00:00-00:00." - }, - "toTime" : { - "type" : "string", - "description" : "toTime: RFC-3339 compliant date time scalar. Returns rewards up to this time. Use the start of the day only, e.g. 2024-01-19T00:00:00-00:00." - } - }, - "required" : [ "customerid", "fromTime", "toTime" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query Rewards($customerid: Int!$fromTime: DateTime!$toTime: DateTime!) {\nRewards(customerid: $customerid, fromTime: $fromTime, toTime: $toTime) {\ntransactionId\ncustomerid\ncardNo\ncardType\ntime\namount\nreward\nmerchantName\n}\n\n}" - } -}, { - "function" : { - "name" : "RewardsByWeek", - "description" : "Returns the total awards a customer earned by week starting from the most recent week.", - "parameters" : { - "type" : "object", - "properties" : { - "offset" : { - "type" : "integer", - "description" : "offset: The number of weeks to offset. For example, if offset is 4, it will skip the last 4 weeks of rewards earned and return the weeks before that." - }, - "customerid" : { - "type" : "integer", - "description" : "customerid: Customer identifier" - }, - "limit" : { - "type" : "integer", - "description" : "limit: The number of weeks to return starting from most recent to less recent weeks. For example, if limit is 12 it will return the last 12 weeks of total rewards earned." - } - }, - "required" : [ "customerid" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query RewardsByWeek($customerid: Int!$limit: Int = 12$offset: Int = 0) {\nRewardsByWeek(customerid: $customerid, limit: $limit, offset: $offset) {\ncustomerid\ntimeWeek\ntotal_reward\n}\n\n}" - } -}, { - "function" : { - "name" : "TotalReward", - "description" : "Returns the total amount of rewards the customer has earned to date and the time since when they eared rewards", - "parameters" : { - "type" : "object", - "properties" : { - "customerid" : { - "type" : "integer", - "description" : "customerid: Customer identifier" - } - }, - "required" : [ "customerid" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query TotalReward($customerid: Int!) {\nTotalReward(customerid: $customerid) {\ncustomerid\ntotal_reward\nsince_time\n}\n\n}" - } -}, { - "function" : { - "name" : "PotentialRewards", - "description" : "Returns all the potential rewards a customer could have earned in the given time period for the given card type. Use this function to show customers the rewards they would have earned if they had the given card.", - "parameters" : { - "type" : "object", - "properties" : { - "customerid" : { - "type" : "integer", - "description" : "customerid: Customer identifier" - }, - "cardType" : { - "type" : "string", - "description" : "cardType: The type of card to calculate potential rewards for (i.e. travel, sports, business, or family)" - }, - "fromTime" : { - "type" : "string", - "description" : "fromTime: RFC-3339 compliant date time scalar. Returns rewards after this time. Use the start of the day only, e.g. 2024-01-19T00:00:00-00:00." - }, - "toTime" : { - "type" : "string", - "description" : "toTime: RFC-3339 compliant date time scalar. Returns rewards up to this time. Use the start of the day only, e.g. 2024-01-19T00:00:00-00:00." - } - }, - "required" : [ "customerid", "cardType", "fromTime", "toTime" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query PotentialRewards($customerid: Int!$cardType: String!$fromTime: DateTime!$toTime: DateTime!) {\nPotentialRewards(customerid: $customerid, cardType: $cardType, fromTime: $fromTime, toTime: $toTime) {\ntransactionId\ncustomerid\nrewardCardType\ntime\namount\nreward\nmerchantName\n}\n\n}" - } -}, { - "function" : { - "name" : "PotentialRewardsByWeek", - "description" : "Returns the total awards a customer could have earned for a given card type by week starting from the most recent week. Use this function to show the customer what their reward earnings would have looked like, if they had a given card.", - "parameters" : { - "type" : "object", - "properties" : { - "offset" : { - "type" : "integer", - "description" : "offset: The number of weeks to offset. For example, if offset is 4, it will skip the last 4 weeks of rewards earned and return the weeks before that." - }, - "customerid" : { - "type" : "integer", - "description" : "customerid: Customer identifier" - }, - "cardType" : { - "type" : "string", - "description" : "cardType: The type of card to calculate potential rewards for (e.g., travel, sports, business, family)" - }, - "limit" : { - "type" : "integer", - "description" : "limit: The number of weeks to return starting from most recent to less recent weeks. For example, if limit is 12 it will return the last 12 weeks of total rewards earned." - } - }, - "required" : [ "customerid", "cardType" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query PotentialRewardsByWeek($customerid: Int!$cardType: String!$limit: Int = 12$offset: Int = 0) {\nPotentialRewardsByWeek(customerid: $customerid, cardType: $cardType, limit: $limit, offset: $offset) {\ncustomerid\ncardType\ntimeWeek\ntotal_reward\n}\n\n}" - } -}, { - "function" : { - "name" : "TotalPotentialReward", - "description" : "Returns the total amount of rewards the customer could have earned for each type of credit card the customer does not yet have. Use this function to determine which credit card type to recommend to a customer.", - "parameters" : { - "type" : "object", - "properties" : { - "customerid" : { - "type" : "integer", - "description" : "customerid: Customer identifier" - } - }, - "required" : [ "customerid" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query TotalPotentialReward($customerid: Int!) {\nTotalPotentialReward(customerid: $customerid) {\ncustomerid\ncardType\ntotal_reward\nsince_time\n}\n\n}" - } -} ] \ No newline at end of file diff --git a/acorn-graphql/src/test/resources/snapshot/law_enforcement.json b/acorn-graphql/src/test/resources/snapshot/law_enforcement.json deleted file mode 100644 index 5ec9133..0000000 --- a/acorn-graphql/src/test/resources/snapshot/law_enforcement.json +++ /dev/null @@ -1,228 +0,0 @@ -[ { - "function" : { - "name" : "BoloDetails", - "description" : "Fetches Bolo details with specified vehicle characteristics.", - "parameters" : { - "type" : "object", - "properties" : { - "offset" : { - "type" : "integer", - "description" : "The number of results to skip before starting to return results. Defaults to 0." - }, - "limit" : { - "type" : "integer", - "description" : "The maximum number of results to return. Defaults to 10." - }, - "model" : { - "type" : "string", - "description" : "The model of the vehicle." - }, - "make" : { - "type" : "string", - "description" : "The make of the vehicle (e.g., Ford, Toyota)." - } - }, - "required" : [ "make" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query BoloDetails($make: String!$model: String$limit: Int = 10$offset: Int = 0) {\nBoloDetails(make: $make, model: $model, limit: $limit, offset: $offset) {\nbolo_id\nvehicle_id\nissue_date\nstatus\nlast_updated\nmake\nmodel\nyear\nregistration_state\nregistration_number\nlicense_state\ndriver_id\n}\n\n}" - } -}, { - "function" : { - "name" : "Driver", - "description" : "Retrieves driver information using their license number.", - "parameters" : { - "type" : "object", - "properties" : { - "vehicles_tracking_limit" : { - "type" : "integer" - }, - "offset" : { - "type" : "integer", - "description" : "The number of results to skip before starting to return results. Defaults to 0." - }, - "bolos_limit" : { - "type" : "integer" - }, - "bolos_offset" : { - "type" : "integer" - }, - "vehicles_limit" : { - "type" : "integer" - }, - "license_number" : { - "type" : "string", - "description" : "The license number of the driver." - }, - "vehicles_bolos_limit" : { - "type" : "integer" - }, - "vehicles_bolos_offset" : { - "type" : "integer" - }, - "warrants_limit" : { - "type" : "integer" - }, - "vehicles_tracking_offset" : { - "type" : "integer" - }, - "limit" : { - "type" : "integer", - "description" : "The maximum number of results to return. Defaults to 10." - }, - "vehicles_offset" : { - "type" : "integer" - }, - "warrants_offset" : { - "type" : "integer" - } - }, - "required" : [ "license_number" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query Driver($license_number: String!$limit: Int = 10$offset: Int = 0$bolos_limit: Int = 10$bolos_offset: Int = 0$vehicles_limit: Int = 10$vehicles_offset: Int = 0$vehicles_bolos_limit: Int = 10$vehicles_bolos_offset: Int = 0$vehicles_tracking_limit: Int = 10$vehicles_tracking_offset: Int = 0$warrants_limit: Int = 10$warrants_offset: Int = 0) {\nDriver(license_number: $license_number, limit: $limit, offset: $offset) {\ndriver_id\nfirst_name\nlast_name\nlicense_number\nlicense_state\ndate_of_birth\nlicense_status\nlicense_expiry_date\nlast_updated\nbolos(limit: $bolos_limit, offset: $bolos_offset) {\nbolo_id\nvehicle_id\nissue_date\nstatus\nlast_updated\nmake\nmodel\nyear\nregistration_state\nregistration_number\nlicense_state\ndriver_id\n}\nvehicles(limit: $vehicles_limit, offset: $vehicles_offset) {\nvehicle_id\nregistration_number\nregistration_state\nregistration_expiry\nmake\nmodel\nyear\nowner_driver_id\nlast_updated\nbolos(limit: $vehicles_bolos_limit, offset: $vehicles_bolos_offset) {\nbolo_id\nvehicle_id\nissue_date\nstatus\nlast_updated\nmake\nmodel\nyear\nregistration_state\nregistration_number\nlicense_state\ndriver_id\n}\ntracking(limit: $vehicles_tracking_limit, offset: $vehicles_tracking_offset) {\nlatitude\nlongitude\nevent_time\n}\n}\nwarrants(limit: $warrants_limit, offset: $warrants_offset) {\nwarrant_id\nperson_id\nwarrant_status\ncrime_description\nstate_of_issuance\nissue_date\nlast_updated\n}\n}\n\n}" - } -}, { - "function" : { - "name" : "Vehicle", - "description" : "Retrieves vehicle details using the registration number.", - "parameters" : { - "type" : "object", - "properties" : { - "offset" : { - "type" : "integer", - "description" : "The number of results to skip before starting to return results. Defaults to 0." - }, - "registration_number" : { - "type" : "string", - "description" : "The registration number of the vehicle, i.e. the license plate number." - }, - "limit" : { - "type" : "integer", - "description" : "The maximum number of results to return. Defaults to 10." - }, - "tracking_limit" : { - "type" : "integer" - }, - "bolos_limit" : { - "type" : "integer" - }, - "tracking_offset" : { - "type" : "integer" - }, - "bolos_offset" : { - "type" : "integer" - } - }, - "required" : [ "registration_number" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query Vehicle($registration_number: String!$limit: Int = 10$offset: Int = 0$bolos_limit: Int = 10$bolos_offset: Int = 0$tracking_limit: Int = 10$tracking_offset: Int = 0) {\nVehicle(registration_number: $registration_number, limit: $limit, offset: $offset) {\nvehicle_id\nregistration_number\nregistration_state\nregistration_expiry\nmake\nmodel\nyear\nowner_driver_id\nlast_updated\nbolos(limit: $bolos_limit, offset: $bolos_offset) {\nbolo_id\nvehicle_id\nissue_date\nstatus\nlast_updated\nmake\nmodel\nyear\nregistration_state\nregistration_number\nlicense_state\ndriver_id\n}\ntracking(limit: $tracking_limit, offset: $tracking_offset) {\nlatitude\nlongitude\nevent_time\n}\n}\n\n}" - } -}, { - "function" : { - "name" : "WarrantsByCrime", - "description" : "Fetches statistics on warrants filtered by the type of crime.", - "parameters" : { - "type" : "object", - "properties" : { - "offset" : { - "type" : "integer" - }, - "limit" : { - "type" : "integer" - }, - "crime" : { - "type" : "string", - "description" : "The type of crime." - } - }, - "required" : [ ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query WarrantsByCrime($crime: String$limit: Int = 100$offset: Int = 0) {\nWarrantsByCrime(crime: $crime, limit: $limit, offset: $offset) {\ncrime\nnum_warrants\n}\n\n}" - } -}, { - "function" : { - "name" : "WarrantsByState", - "description" : "Fetches statistics on warrants issued by state filtered by their status.", - "parameters" : { - "type" : "object", - "properties" : { - "offset" : { - "type" : "integer" - }, - "limit" : { - "type" : "integer" - }, - "status" : { - "type" : "string", - "description" : "The status of the warrant (one of: active, urgent, closed, suspended)." - } - }, - "required" : [ ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query WarrantsByState($status: String$limit: Int = 100$offset: Int = 0) {\nWarrantsByState(status: $status, limit: $limit, offset: $offset) {\nstate\nstatus\nnum_warrants\n}\n\n}" - } -}, { - "function" : { - "name" : "BolosByWeekState", - "description" : "Fetches Bolo (be on the lookout) statistics by week filtered by state. It returns the data ordered\nby week starting from the most recent and going backwards from there.", - "parameters" : { - "type" : "object", - "properties" : { - "offset" : { - "type" : "integer", - "description" : "The number of results to skip before starting to return results. Defaults to 0." - }, - "limit" : { - "type" : "integer", - "description" : "The maximum number of results to return. Defaults to 100. Set to the number of past weeks to return\nmultiplied by the number of states." - }, - "state" : { - "type" : "string", - "description" : "The state for which Bolos are to be fetched abbreviated to two uppercase letters (e.g. WA, CA)." - } - }, - "required" : [ ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query BolosByWeekState($state: String$limit: Int = 100$offset: Int = 0) {\nBolosByWeekState(state: $state, limit: $limit, offset: $offset) {\nweek\nstate\nnum_bolos\n}\n\n}" - } -}, { - "function" : { - "name" : "Tracking", - "parameters" : { - "type" : "object", - "properties" : { - "latitude" : { - "type" : "number" - }, - "plate" : { - "type" : "string" - }, - "longitude" : { - "type" : "number" - } - }, - "required" : [ "plate", "latitude", "longitude" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "mutation Tracking($plate: String!$latitude: Float!$longitude: Float!) {\nTracking(encounter: { plate: $plate, latitude: $latitude, longitude: $longitude }) {\n_uuid\nplate\n}\n\n}" - } -} ] \ No newline at end of file diff --git a/acorn-graphql/src/test/resources/snapshot/nutshop.json b/acorn-graphql/src/test/resources/snapshot/nutshop.json deleted file mode 100644 index 4c33c7a..0000000 --- a/acorn-graphql/src/test/resources/snapshot/nutshop.json +++ /dev/null @@ -1,151 +0,0 @@ -[ { - "function" : { - "name" : "SpendingByWeek", - "description" : " Retrieve weekly spending data for a specified customer.", - "parameters" : { - "type" : "object", - "properties" : { - "offset" : { - "type" : "integer", - "description" : " The number of results to skip before starting to collect the result set (default: 0)." - }, - "customerid" : { - "type" : "integer", - "description" : "customerid: The unique identifier for the customer." - }, - "limit" : { - "type" : "integer", - "description" : " The maximum number of results to return (default: 10)." - } - }, - "required" : [ "customerid" ] - } - }, - "contextKeys" : [ "customerid" ], - "apiQuery" : { - "query" : "query SpendingByWeek($customerid: Int!$limit: Int = 10$offset: Int = 0) {\nSpendingByWeek(customerid: $customerid, limit: $limit, offset: $offset) {\nweek\ntotal_spend\ntotal_savings\n}\n\n}" - } -}, { - "function" : { - "name" : "Products", - "description" : " Retrieve product information with optional filtering by product ID.", - "parameters" : { - "type" : "object", - "properties" : { - "orders_limit" : { - "type" : "integer" - }, - "offset" : { - "type" : "integer", - "description" : " The number of results to skip before starting to collect the result set (default: 0)." - }, - "limit" : { - "type" : "integer", - "description" : " The maximum number of results to return (default: 10)." - }, - "id" : { - "type" : "integer", - "description" : " The unique identifier for the product (optional)." - }, - "orders_items_limit" : { - "type" : "integer" - } - }, - "required" : [ ] - } - }, - "contextKeys" : [ "customerid" ], - "apiQuery" : { - "query" : "query Products($id: Int$limit: Int = 10$offset: Int = 0$orders_limit: Int = 10$orders_items_limit: Int = 10) {\nProducts(id: $id, limit: $limit, offset: $offset) {\nid\nname\nsizing\nweight_in_gram\ntype\ncategory\nusda_id\nupdated\norders(limit: $orders_limit) {\nid\ncustomerid\ntimestamp\nitems(limit: $orders_items_limit) {\nquantity\nunit_price\ndiscount0\ntotal\n}\ntotal {\nprice\ndiscount\n}\n}\n}\n\n}" - } -}, { - "function" : { - "name" : "Orders", - "description" : " Retrieve order information for a specified customer.", - "parameters" : { - "type" : "object", - "properties" : { - "offset" : { - "type" : "integer", - "description" : " The number of results to skip before starting to collect the result set (default: 0)." - }, - "customerid" : { - "type" : "integer", - "description" : " The unique identifier for the customer." - }, - "limit" : { - "type" : "integer", - "description" : " The maximum number of results to return (default: 10)." - }, - "items_limit" : { - "type" : "integer" - } - }, - "required" : [ "customerid" ] - } - }, - "contextKeys" : [ "customerid" ], - "apiQuery" : { - "query" : "query Orders($customerid: Int!$limit: Int = 10$offset: Int = 0$items_limit: Int = 10) {\nOrders(customerid: $customerid, limit: $limit, offset: $offset) {\nid\ncustomerid\ntimestamp\nitems(limit: $items_limit) {\nquantity\nunit_price\ndiscount0\ntotal\nproduct {\nid\nname\nsizing\nweight_in_gram\ntype\ncategory\nusda_id\nupdated\n}\n}\ntotal {\nprice\ndiscount\n}\n}\n\n}" - } -}, { - "function" : { - "name" : "OrdersByTimeRange", - "description" : "Retrieves orders for a given customer within the specified time range", - "parameters" : { - "type" : "object", - "properties" : { - "customerid" : { - "type" : "integer", - "description" : "The unique identifier for the customer" - }, - "fromTime" : { - "type" : "string", - "description" : "Returns orders where the order timestamp is larger than this RFC-3339 compliant fromTime timestamp" - }, - "items_limit" : { - "type" : "integer" - }, - "toTime" : { - "type" : "string", - "description" : "Returns orders where the order timestamp is smaller than this RFC-3339 compliant toTime timestamp" - } - }, - "required" : [ "customerid", "fromTime", "toTime" ] - } - }, - "contextKeys" : [ "customerid" ], - "apiQuery" : { - "query" : "query OrdersByTimeRange($customerid: Int!$fromTime: DateTime!$toTime: DateTime!$items_limit: Int = 10) {\nOrdersByTimeRange(customerid: $customerid, fromTime: $fromTime, toTime: $toTime) {\nid\ncustomerid\ntimestamp\nitems(limit: $items_limit) {\nquantity\nunit_price\ndiscount0\ntotal\nproduct {\nid\nname\nsizing\nweight_in_gram\ntype\ncategory\nusda_id\nupdated\n}\n}\ntotal {\nprice\ndiscount\n}\n}\n\n}" - } -}, { - "function" : { - "name" : "OrderAgain", - "description" : " Suggest products for a customer to order again based on their previous orders.", - "parameters" : { - "type" : "object", - "properties" : { - "offset" : { - "type" : "integer", - "description" : " The number of results to skip before starting to collect the result set (default: 0)." - }, - "customerid" : { - "type" : "integer", - "description" : " The unique identifier for the customer." - }, - "limit" : { - "type" : "integer", - "description" : " The maximum number of results to return (default: 10)." - }, - "product_orders_limit" : { - "type" : "integer" - } - }, - "required" : [ "customerid" ] - } - }, - "contextKeys" : [ "customerid" ], - "apiQuery" : { - "query" : "query OrderAgain($customerid: Int!$limit: Int = 10$offset: Int = 0$product_orders_limit: Int = 10) {\nOrderAgain(customerid: $customerid, limit: $limit, offset: $offset) {\nproduct {\nid\nname\nsizing\nweight_in_gram\ntype\ncategory\nusda_id\nupdated\norders(limit: $product_orders_limit) {\nid\ncustomerid\ntimestamp\n}\n}\nnum\nquantity\n}\n\n}" - } -} ] \ No newline at end of file diff --git a/acorn-graphql/src/test/resources/snapshot/rick-morty.json b/acorn-graphql/src/test/resources/snapshot/rick-morty.json deleted file mode 100644 index f430968..0000000 --- a/acorn-graphql/src/test/resources/snapshot/rick-morty.json +++ /dev/null @@ -1,202 +0,0 @@ -[ { - "function" : { - "name" : "character", - "description" : "Get a specific character by ID", - "parameters" : { - "type" : "object", - "properties" : { - "id" : { - "type" : "string" - } - }, - "required" : [ "id" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query character($id: ID!) {\ncharacter(id: $id) {\nid\nname\nstatus\nspecies\ntype\ngender\norigin {\nid\nname\ntype\ndimension\ncreated\n}\nlocation {\nid\nname\ntype\ndimension\ncreated\n}\nimage\nepisode {\nid\nname\nair_date\nepisode\ncreated\n}\ncreated\n}\n\n}" - } -}, { - "function" : { - "name" : "characters", - "description" : "Get the list of all characters", - "parameters" : { - "type" : "object", - "properties" : { - "gender" : { - "type" : "string" - }, - "species" : { - "type" : "string" - }, - "name" : { - "type" : "string" - }, - "page" : { - "type" : "integer" - }, - "type" : { - "type" : "string" - }, - "status" : { - "type" : "string" - } - }, - "required" : [ ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query characters($page: Int$name: String$status: String$species: String$type: String$gender: String) {\ncharacters(page: $page, filter: { name: $name, status: $status, species: $species, type: $type, gender: $gender }) {\ninfo {\ncount\npages\nnext\nprev\n}\nresults {\nid\nname\nstatus\nspecies\ntype\ngender\norigin {\nid\nname\ntype\ndimension\ncreated\n}\nlocation {\nid\nname\ntype\ndimension\ncreated\n}\nimage\nepisode {\nid\nname\nair_date\nepisode\ncreated\n}\ncreated\n}\n}\n\n}" - } -}, { - "function" : { - "name" : "charactersByIds", - "description" : "Get a list of characters selected by ids", - "parameters" : { - "type" : "object", - "properties" : { - "ids" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - }, - "required" : [ "ids" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query charactersByIds($ids: [ID!]!) {\ncharactersByIds(ids: $ids) {\nid\nname\nstatus\nspecies\ntype\ngender\norigin {\nid\nname\ntype\ndimension\ncreated\n}\nlocation {\nid\nname\ntype\ndimension\ncreated\n}\nimage\nepisode {\nid\nname\nair_date\nepisode\ncreated\n}\ncreated\n}\n\n}" - } -}, { - "function" : { - "name" : "location", - "description" : "Get a specific locations by ID", - "parameters" : { - "type" : "object", - "properties" : { - "id" : { - "type" : "string" - } - }, - "required" : [ "id" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query location($id: ID!) {\nlocation(id: $id) {\nid\nname\ntype\ndimension\nresidents {\nid\nname\nstatus\nspecies\ntype\ngender\nimage\nepisode {\nid\nname\nair_date\nepisode\ncreated\n}\ncreated\n}\ncreated\n}\n\n}" - } -}, { - "function" : { - "name" : "locations", - "description" : "Get the list of all locations", - "parameters" : { - "type" : "object", - "properties" : { - "name" : { - "type" : "string" - }, - "page" : { - "type" : "integer" - }, - "type" : { - "type" : "string" - }, - "dimension" : { - "type" : "string" - } - }, - "required" : [ ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query locations($page: Int$name: String$type: String$dimension: String) {\nlocations(page: $page, filter: { name: $name, type: $type, dimension: $dimension }) {\ninfo {\ncount\npages\nnext\nprev\n}\nresults {\nid\nname\ntype\ndimension\nresidents {\nid\nname\nstatus\nspecies\ntype\ngender\nimage\ncreated\n}\ncreated\n}\n}\n\n}" - } -}, { - "function" : { - "name" : "locationsByIds", - "description" : "Get a list of locations selected by ids", - "parameters" : { - "type" : "object", - "properties" : { - "ids" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - }, - "required" : [ "ids" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query locationsByIds($ids: [ID!]!) {\nlocationsByIds(ids: $ids) {\nid\nname\ntype\ndimension\nresidents {\nid\nname\nstatus\nspecies\ntype\ngender\nimage\nepisode {\nid\nname\nair_date\nepisode\ncreated\n}\ncreated\n}\ncreated\n}\n\n}" - } -}, { - "function" : { - "name" : "episode", - "description" : "Get a specific episode by ID", - "parameters" : { - "type" : "object", - "properties" : { - "id" : { - "type" : "string" - } - }, - "required" : [ "id" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query episode($id: ID!) {\nepisode(id: $id) {\nid\nname\nair_date\nepisode\ncharacters {\nid\nname\nstatus\nspecies\ntype\ngender\norigin {\nid\nname\ntype\ndimension\ncreated\n}\nlocation {\nid\nname\ntype\ndimension\ncreated\n}\nimage\ncreated\n}\ncreated\n}\n\n}" - } -}, { - "function" : { - "name" : "episodes", - "description" : "Get the list of all episodes", - "parameters" : { - "type" : "object", - "properties" : { - "name" : { - "type" : "string" - }, - "episode" : { - "type" : "string" - }, - "page" : { - "type" : "integer" - } - }, - "required" : [ ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query episodes($page: Int$name: String$episode: String) {\nepisodes(page: $page, filter: { name: $name, episode: $episode }) {\ninfo {\ncount\npages\nnext\nprev\n}\nresults {\nid\nname\nair_date\nepisode\ncharacters {\nid\nname\nstatus\nspecies\ntype\ngender\nimage\ncreated\n}\ncreated\n}\n}\n\n}" - } -}, { - "function" : { - "name" : "episodesByIds", - "description" : "Get a list of episodes selected by ids", - "parameters" : { - "type" : "object", - "properties" : { - "ids" : { - "type" : "array", - "items" : { - "type" : "string" - } - } - }, - "required" : [ "ids" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query episodesByIds($ids: [ID!]!) {\nepisodesByIds(ids: $ids) {\nid\nname\nair_date\nepisode\ncharacters {\nid\nname\nstatus\nspecies\ntype\ngender\norigin {\nid\nname\ntype\ndimension\ncreated\n}\nlocation {\nid\nname\ntype\ndimension\ncreated\n}\nimage\ncreated\n}\ncreated\n}\n\n}" - } -} ] \ No newline at end of file diff --git a/acorn-graphql/src/test/resources/snapshot/sensors.json b/acorn-graphql/src/test/resources/snapshot/sensors.json deleted file mode 100644 index 54ff581..0000000 --- a/acorn-graphql/src/test/resources/snapshot/sensors.json +++ /dev/null @@ -1,158 +0,0 @@ -[ { - "function" : { - "name" : "SensorReading", - "description" : "Returns the sensor temperature readings for each second by most recent for a given sensor id", - "parameters" : { - "type" : "object", - "properties" : { - "offset" : { - "type" : "integer" - }, - "limit" : { - "type" : "integer", - "description" : "The number of readings (one per second) to return" - }, - "sensorid" : { - "type" : "integer", - "description" : "The id of the sensor" - } - }, - "required" : [ "sensorid" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query SensorReading($sensorid: Int!$limit: Int = 10$offset: Int = 0) {\nSensorReading(sensorid: $sensorid, limit: $limit, offset: $offset) {\nsensorid\ntemperature\nevent_time\n}\n\n}" - } -}, { - "function" : { - "name" : "ReadingsAboveTemp", - "description" : "Returns all sensor temperature readings above the given temperature", - "parameters" : { - "type" : "object", - "properties" : { - "temp" : { - "type" : "number" - }, - "limit" : { - "type" : "integer" - } - }, - "required" : [ "temp" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query ReadingsAboveTemp($temp: Float!$limit: Int = 10) {\nReadingsAboveTemp(temp: $temp, limit: $limit) {\nsensorid\ntemperature\nevent_time\n}\n\n}" - } -}, { - "function" : { - "name" : "SensorMaxTempLastMinute", - "description" : "Returns the maximum temperature recorded by each sensor in the last minute", - "parameters" : { - "type" : "object", - "properties" : { - "offset" : { - "type" : "integer" - }, - "limit" : { - "type" : "integer", - "description" : "The number sensors to return max temperature for" - }, - "sensorid" : { - "type" : "integer", - "description" : "The id of the sensor. If left empty, returns max temperature for all sensors." - } - }, - "required" : [ ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query SensorMaxTempLastMinute($sensorid: Int$limit: Int = 10$offset: Int = 0) {\nSensorMaxTempLastMinute(sensorid: $sensorid, limit: $limit, offset: $offset) {\nsensorid\nmaxTemp\nlast_updated\n}\n\n}" - } -}, { - "function" : { - "name" : "SensorMaxTemp", - "description" : "Returns the maximum temperature recorded by each sensor", - "parameters" : { - "type" : "object", - "properties" : { - "offset" : { - "type" : "integer" - }, - "limit" : { - "type" : "integer", - "description" : "The number sensors to return max temperature for" - }, - "sensorid" : { - "type" : "integer", - "description" : "The id of the sensor. If left empty, returns max temperature for all sensors." - } - }, - "required" : [ ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query SensorMaxTemp($sensorid: Int$limit: Int = 10$offset: Int = 0) {\nSensorMaxTemp(sensorid: $sensorid, limit: $limit, offset: $offset) {\nsensorid\nmaxTemp\nlast_updated\n}\n\n}" - } -}, { - "function" : { - "name" : "AddReading", - "parameters" : { - "type" : "object", - "properties" : { - "temperature" : { - "type" : "number" - }, - "sensorid" : { - "type" : "integer" - } - }, - "required" : [ "sensorid", "temperature" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "mutation AddReading($sensorid: Int!$temperature: Float!) {\nAddReading(metric: { sensorid: $sensorid, temperature: $temperature }) {\nsensorid\ntemperature\nevent_time\n}\n\n}" - } -}, { - "function" : { - "name" : "HighTemps", - "description" : " Returns all readings with a temperature higher than the provided value", - "parameters" : { - "type" : "object", - "properties" : { - "temperature" : { - "type" : "number", - "description" : " The temperature" - } - }, - "required" : [ "temperature" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query HighTemps(\n $temperature: Float!\n) {\n ReadingsAboveTemp(temp: $temperature) {\n sensorid\n temperature\n }\n}\n" - } -}, { - "function" : { - "name" : "HighTemps2", - "description" : " high temperature readings", - "parameters" : { - "type" : "object", - "properties" : { - "temp" : { - "type" : "number", - "description" : "" - } - }, - "required" : [ "temp" ] - } - }, - "contextKeys" : [ ], - "apiQuery" : { - "query" : "query HighTemps2(\n $temp: Float!\n) {\n ReadingsAboveTemp(temp: $temp) {\n sensorid\n temperature\n }\n}\n" - } -} ] \ No newline at end of file diff --git a/acorn-springai/pom.xml b/acorn-springai/pom.xml index 8a7e994..804ac7d 100644 --- a/acorn-springai/pom.xml +++ b/acorn-springai/pom.xml @@ -12,9 +12,9 @@ - ${project.groupId} - acorn-graphql - ${project.version} + com.datasqrl + sqrl-graphql + ${sqrl.version} diff --git a/coverage/pom.xml b/coverage/pom.xml index a527df0..738aa34 100644 --- a/coverage/pom.xml +++ b/coverage/pom.xml @@ -17,11 +17,6 @@ acorn-springai ${project.version} - - ${project.groupId} - acorn-graphql - ${project.version} - ${project.groupId} acorn-sqrl-creditcard-rewards diff --git a/pom.xml b/pom.xml index ba96176..069181c 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,6 @@ - acorn-graphql acorn-springai acorn-examples coverage @@ -76,10 +75,25 @@ 3.3.0 1.4.0 2.2.224 + 0.7.0-SNAPSHOT + 5.3 3.5.2 + + + + false + + + true + + github + https://maven.pkg.github.com/DataSQRL/sqrl + + +