diff --git a/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/OpenaiClient.java b/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/OpenaiClient.java index 7c642432..54f07c80 100644 --- a/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/OpenaiClient.java +++ b/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/OpenaiClient.java @@ -10,8 +10,8 @@ import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -import de.l3s.interweb.connector.openai.entity.CompletionBody; -import de.l3s.interweb.connector.openai.entity.CompletionResponse; +import de.l3s.interweb.connector.openai.entity.CompletionsBody; +import de.l3s.interweb.connector.openai.entity.CompletionsResponse; import de.l3s.interweb.core.ConnectorException; @Path("/openai/deployments") @@ -19,7 +19,7 @@ @Produces(MediaType.APPLICATION_JSON) @RegisterRestClient(configKey = "openai") @ClientHeaderParam(name = "api-key", value = "${connector.openai.apikey}") -@ClientQueryParam(name = "api-version", value = "2024-02-01") +@ClientQueryParam(name = "api-version", value = "2024-06-01") public interface OpenaiClient { /** @@ -28,7 +28,7 @@ public interface OpenaiClient { */ @POST @Path("/{model}/chat/completions") - Uni chatCompletions(@PathParam("model") String model, CompletionBody body); + Uni chatCompletions(@PathParam("model") String model, CompletionsBody body); @ClientExceptionMapper static RuntimeException toException(Response response) { diff --git a/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/OpenaiConnector.java b/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/OpenaiConnector.java index 1ea3c93e..358bd09f 100644 --- a/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/OpenaiConnector.java +++ b/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/OpenaiConnector.java @@ -11,7 +11,7 @@ import org.eclipse.microprofile.rest.client.inject.RestClient; import org.jboss.logging.Logger; -import de.l3s.interweb.connector.openai.entity.CompletionBody; +import de.l3s.interweb.connector.openai.entity.CompletionsBody; import de.l3s.interweb.core.ConnectorException; import de.l3s.interweb.core.chat.ChatConnector; import de.l3s.interweb.core.chat.CompletionsQuery; @@ -56,7 +56,7 @@ public Uni> getModels() { @Override public Uni completions(CompletionsQuery query) throws ConnectorException { - return openai.chatCompletions(query.getModel(), new CompletionBody(query)).map(response -> { + return openai.chatCompletions(query.getModel(), new CompletionsBody(query)).map(response -> { CompletionsResults results = new CompletionsResults(); results.setModel(query.getModel()); results.setCreated(response.getCreated()); diff --git a/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionBody.java b/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionsBody.java similarity index 69% rename from connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionBody.java rename to connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionsBody.java index b95effec..47e015cb 100644 --- a/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionBody.java +++ b/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionsBody.java @@ -9,12 +9,13 @@ import de.l3s.interweb.core.chat.CompletionsQuery; import de.l3s.interweb.core.chat.ResponseFormat; +import de.l3s.interweb.core.chat.Tool; @RegisterForReflection @JsonInclude(JsonInclude.Include.NON_NULL) -public final class CompletionBody { +public final class CompletionsBody { - private List messages; + private List messages; private Double temperature; @@ -30,26 +31,25 @@ public final class CompletionBody { @JsonProperty("max_tokens") private Integer maxTokens; - /** - * How many completions to generate for each prompt. Minimum of 1 (default) and maximum of 128 allowed. - * Note: Because this parameter generates many completions, it can quickly consume your token quota. - */ private Integer n; - /** - * If specified, our system will make the best effort to sample deterministically, - * such that repeated requests with the same seed and parameters should return the same result. - * Determinism isn't guaranteed, and you should refer to the system_fingerprint response parameter to monitor changes in the backend. - */ private Integer seed; @JsonProperty("response_format") private ResponseFormat responseFormat; + private List tools; + + @JsonProperty("tool_choice") + private Object toolChoice; + + @JsonProperty("parallel_tool_calls") + private Boolean parallelToolCalls; + private String[] stop; - public CompletionBody(CompletionsQuery query) { - this.messages = query.getMessages().stream().map(CompletionMessage::new).toList(); + public CompletionsBody(CompletionsQuery query) { + this.messages = query.getMessages().stream().map(OpenaiMessage::new).toList(); this.temperature = query.getTemperature(); this.topP = query.getTopP(); this.frequencyPenalty = query.getPresencePenalty(); @@ -59,9 +59,12 @@ public CompletionBody(CompletionsQuery query) { this.seed = query.getSeed(); this.responseFormat = query.getResponseFormat(); this.stop = query.getStop(); + this.tools = query.getTools(); + this.toolChoice = query.getToolChoice(); + this.parallelToolCalls = query.getParallelToolCalls(); } - public List getMessages() { + public List getMessages() { return messages; } @@ -100,4 +103,16 @@ public ResponseFormat getResponseFormat() { public String[] getStop() { return stop; } + + public List getTools() { + return tools; + } + + public Object getToolChoice() { + return toolChoice; + } + + public Boolean getParallelToolCalls() { + return parallelToolCalls; + } } diff --git a/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionResponse.java b/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionsResponse.java similarity index 97% rename from connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionResponse.java rename to connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionsResponse.java index 4c8cbcd0..124b69da 100644 --- a/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionResponse.java +++ b/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionsResponse.java @@ -11,7 +11,7 @@ import de.l3s.interweb.core.chat.Usage; @RegisterForReflection -public class CompletionResponse { +public class CompletionsResponse { private String id; private String object; private String model; diff --git a/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionMessage.java b/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/OpenaiMessage.java similarity index 51% rename from connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionMessage.java rename to connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/OpenaiMessage.java index 92b40eba..6bfab970 100644 --- a/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/CompletionMessage.java +++ b/connectors/OpenaiConnector/src/main/java/de/l3s/interweb/connector/openai/entity/OpenaiMessage.java @@ -1,45 +1,52 @@ package de.l3s.interweb.connector.openai.entity; +import java.util.List; + import io.quarkus.runtime.annotations.RegisterForReflection; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; import de.l3s.interweb.core.chat.Message; +import de.l3s.interweb.core.chat.ToolCall; @RegisterForReflection -public final class CompletionMessage { +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class OpenaiMessage { private String role; @JsonIgnore private String name; private String content; + private String refusal; + @JsonProperty("tool_calls") + private List toolCalls; - public CompletionMessage(Message message) { + public OpenaiMessage(Message message) { this.role = message.getRole().name(); this.name = message.getName(); this.content = message.getContent(); + this.refusal = message.getRefusal(); + this.toolCalls = message.getToolCalls(); } public String getRole() { return role; } - public void setRole(String role) { - this.role = role; - } - public String getName() { return name; } - public void setName(String name) { - this.name = name; - } - public String getContent() { return content; } - public void setContent(String content) { - this.content = content; + public String getRefusal() { + return refusal; + } + + public List getToolCalls() { + return toolCalls; } } diff --git a/connectors/OpenaiConnector/src/test/java/de/l3s/interweb/connector/openai/OpenaiConnectorTest.java b/connectors/OpenaiConnector/src/test/java/de/l3s/interweb/connector/openai/OpenaiConnectorTest.java index 0a121447..749268a7 100644 --- a/connectors/OpenaiConnector/src/test/java/de/l3s/interweb/connector/openai/OpenaiConnectorTest.java +++ b/connectors/OpenaiConnector/src/test/java/de/l3s/interweb/connector/openai/OpenaiConnectorTest.java @@ -2,6 +2,8 @@ import static org.junit.jupiter.api.Assertions.*; +import java.util.List; + import jakarta.inject.Inject; import io.quarkus.test.junit.QuarkusTest; @@ -10,10 +12,7 @@ import org.junit.jupiter.api.Test; import de.l3s.interweb.core.ConnectorException; -import de.l3s.interweb.core.chat.Choice; -import de.l3s.interweb.core.chat.CompletionsQuery; -import de.l3s.interweb.core.chat.CompletionsResults; -import de.l3s.interweb.core.chat.Role; +import de.l3s.interweb.core.chat.*; @Disabled @QuarkusTest @@ -43,4 +42,27 @@ void completions() throws ConnectorException { log.infov("assistant: {0}", result.getMessage().getContent()); } } + + @Test + void completionsWithTool() throws ConnectorException { + Tool weatherTool = Tool.functionBuilder() + .name("get_weather") + .description("Return the weather in a city.") + .parameters(city -> city.name("city").type("string").description("The city name.").required()) + .build(); + + CompletionsQuery query = new CompletionsQuery(); + query.setModel("gpt-4"); + query.setTools(List.of(weatherTool)); + query.setToolChoice(ToolChoice.required); + query.addMessage("You are Interweb Assistant, a helpful chat bot.", Role.system); + query.addMessage("What is the weather in Hannover?", Role.user); + + CompletionsResults results = connector.completions(query).await().indefinitely(); + + assertEquals(1, results.getChoices().size()); + for (Choice result : results.getChoices()) { + assertEquals("get_weather", result.getMessage().getToolCalls().getFirst().getFunction().getName()); + } + } } diff --git a/interweb-core/src/main/java/de/l3s/interweb/core/chat/CallFunction.java b/interweb-core/src/main/java/de/l3s/interweb/core/chat/CallFunction.java new file mode 100644 index 00000000..5dfbc823 --- /dev/null +++ b/interweb-core/src/main/java/de/l3s/interweb/core/chat/CallFunction.java @@ -0,0 +1,42 @@ +package de.l3s.interweb.core.chat; + +import java.io.Serial; +import java.io.Serializable; + +import jakarta.validation.constraints.NotEmpty; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection +public class CallFunction implements Serializable { + @Serial + private static final long serialVersionUID = -2780720621585498099L; + + /** + * The name of the function to call. + */ + @NotEmpty + private String name; + + /** + * The arguments to call the function with, as generated by the model in JSON format. Note that the model does not always generate valid JSON, + * and may hallucinate parameters not defined by your function schema. Validate the arguments in your code before calling your function. + */ + private String arguments; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setArguments(String arguments) { + this.arguments = arguments; + } + + public String getArguments() { + return arguments; + } +} diff --git a/interweb-core/src/main/java/de/l3s/interweb/core/chat/Choice.java b/interweb-core/src/main/java/de/l3s/interweb/core/chat/Choice.java index 9046a3d9..ee0a374b 100644 --- a/interweb-core/src/main/java/de/l3s/interweb/core/chat/Choice.java +++ b/interweb-core/src/main/java/de/l3s/interweb/core/chat/Choice.java @@ -10,9 +10,22 @@ @RegisterForReflection @JsonIgnoreProperties({"elapsed_time", "created"}) public class Choice extends ConnectorResults { + /** + * The index of the choice in the list of choices. + */ private int index; + + /** + * The reason the model stopped generating tokens. + * This will be stop if the model hit a natural stop point or a provided stop sequence, length if the maximum number of tokens specified + * in the request was reached, content_filter if content was omitted due to a flag from our content filters, tool_calls if the model called a tool. + */ @JsonProperty("finish_reason") private String finishReason; + + /** + * A chat completion message generated by the model. + */ private Message message; public Choice() { diff --git a/interweb-core/src/main/java/de/l3s/interweb/core/chat/CompletionsQuery.java b/interweb-core/src/main/java/de/l3s/interweb/core/chat/CompletionsQuery.java index b04dcb7a..350f03d3 100644 --- a/interweb-core/src/main/java/de/l3s/interweb/core/chat/CompletionsQuery.java +++ b/interweb-core/src/main/java/de/l3s/interweb/core/chat/CompletionsQuery.java @@ -7,6 +7,7 @@ import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; import io.quarkus.runtime.annotations.RegisterForReflection; @@ -92,6 +93,35 @@ public class CompletionsQuery { @JsonProperty("max_tokens") private Integer maxTokens; + /** + * A list of tools the model may call. Currently, only functions are supported as a tool. + * Use this to provide a list of functions the model may generate JSON inputs for. + *
+ * A max of 128 functions are supported. + */ + @Size(max = 128) + @JsonProperty("tools") + private List tools; + + /** + * Controls which (if any) tool is called by the model. + * - none means the model will not call any tool and instead generates a message. + * - auto means the model can pick between generating a message or calling one or more tools. + * - required means the model must call one or more tools. + * Specifying a particular tool via {"type": "function", "function": {"name": "my_function"}} forces the model to call that tool. + *
+ * none is the default when no tools are present. auto is the default if tools are present. + */ + @JsonProperty("tool_choice") + private Object toolChoice; + + /** + * Whether to enable parallel function calling during tool use. + * https://platform.openai.com/docs/guides/function-calling/parallel-function-calling + */ + @JsonProperty("parallel_tool_calls") + private Boolean parallelToolCalls; + /** * Whether to incrementally stream the response using server-sent events. Defaults to false. */ @@ -230,6 +260,30 @@ public Integer getN() { return n; } + public void setTools(List tools) { + this.tools = tools; + } + + public List getTools() { + return tools; + } + + public void setToolChoice(Object toolChoice) { + this.toolChoice = toolChoice; + } + + public Object getToolChoice() { + return toolChoice; + } + + public void setParallelToolCalls(Boolean parallelToolCalls) { + this.parallelToolCalls = parallelToolCalls; + } + + public Boolean getParallelToolCalls() { + return parallelToolCalls; + } + public void setSeed(Integer seed) { this.seed = seed; } diff --git a/interweb-core/src/main/java/de/l3s/interweb/core/chat/Function.java b/interweb-core/src/main/java/de/l3s/interweb/core/chat/Function.java new file mode 100644 index 00000000..41b1c36a --- /dev/null +++ b/interweb-core/src/main/java/de/l3s/interweb/core/chat/Function.java @@ -0,0 +1,77 @@ +package de.l3s.interweb.core.chat; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@RegisterForReflection +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Function { + + /** + * The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64. + */ + @NotEmpty + @Size(max = 64) + @JsonProperty("name") + private String name; + + /** + * A description of what the function does, used by the model to choose when and how to call the function. + */ + @JsonProperty("description") + private String description; + + /** + * The parameters the function accepts, described as a JSON Schema object. + * Omitting parameters defines a function with an empty parameter list. + * https://platform.openai.com/docs/guides/function-calling + */ + @JsonProperty("parameters") + private Parameters parameters; + + /** + * Whether to enable strict schema adherence when generating the function call. + * If set to true, the model will follow the exact schema defined in the `parameters` field. + * Only a subset of JSON Schema is supported when strict is true. Learn more about Structured Outputs in the function calling guide. + * https://platform.openai.com/docs/api-reference/chat/docs/guides/function-calling + */ + @JsonProperty("strict") + private Boolean strict; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + public void setParameters(Parameters parameters) { + this.parameters = parameters; + } + + public Parameters getParameters() { + return parameters; + } + + public void setStrict(Boolean strict) { + this.strict = strict; + } + + public Boolean getStrict() { + return strict; + } +} diff --git a/interweb-core/src/main/java/de/l3s/interweb/core/chat/FunctionToolBuilder.java b/interweb-core/src/main/java/de/l3s/interweb/core/chat/FunctionToolBuilder.java new file mode 100644 index 00000000..a73e8ce9 --- /dev/null +++ b/interweb-core/src/main/java/de/l3s/interweb/core/chat/FunctionToolBuilder.java @@ -0,0 +1,95 @@ +package de.l3s.interweb.core.chat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +public class FunctionToolBuilder { + private final Function function = new Function(); + + public FunctionToolBuilder name(String name) { + function.setName(name); + return this; + } + + public FunctionToolBuilder description(String description) { + function.setDescription(description); + return this; + } + + public FunctionToolBuilder parameters(Customizer ...parameterCustomizer) { + if (function.getParameters() == null) { + function.setParameters(new Parameters()); + } + + HashMap properties = new HashMap<>(); + ArrayList required = new ArrayList<>(); + for (Customizer customizer : parameterCustomizer) { + PropertyConfigurer propertyConfigurer = new PropertyConfigurer(); + customizer.customize(propertyConfigurer); + + properties.put(propertyConfigurer.name, propertyConfigurer.property); + if (propertyConfigurer.required) { + required.add(propertyConfigurer.name); + } + } + + function.getParameters().setType("object"); + function.getParameters().setProperties(properties); + function.getParameters().setRequired(required); + return this; + } + + public FunctionToolBuilder additionalProperties() { + if (function.getParameters() == null) { + function.setParameters(new Parameters()); + } + + function.getParameters().setAdditionalProperties(true); + return this; + } + + public FunctionToolBuilder strict(Boolean strict) { + function.setStrict(strict); + return this; + } + + public Tool build() { + return new Tool(function); + } + + public interface Customizer { + void customize(T var1); + } + + public static class PropertyConfigurer { + private String name; + private final Property property = new Property(); + private boolean required; + + public PropertyConfigurer name(String name) { + this.name = name; + return this; + } + + public PropertyConfigurer description(String description) { + this.property.setDescription(description); + return this; + } + + public PropertyConfigurer type(String type) { + this.property.setType(type); + return this; + } + + public PropertyConfigurer enumValues(String ...enumValues) { + this.property.setEnumValues(Arrays.asList(enumValues)); + return this; + } + + public PropertyConfigurer required() { + this.required = true; + return this; + } + } +} diff --git a/interweb-core/src/main/java/de/l3s/interweb/core/chat/Message.java b/interweb-core/src/main/java/de/l3s/interweb/core/chat/Message.java index 34590aee..1df80336 100644 --- a/interweb-core/src/main/java/de/l3s/interweb/core/chat/Message.java +++ b/interweb-core/src/main/java/de/l3s/interweb/core/chat/Message.java @@ -3,6 +3,7 @@ import java.io.Serial; import java.io.Serializable; import java.time.Instant; +import java.util.List; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; @@ -10,6 +11,7 @@ import io.quarkus.runtime.annotations.RegisterForReflection; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; @RegisterForReflection @JsonIgnoreProperties(ignoreUnknown = true) @@ -18,11 +20,26 @@ public class Message implements Serializable { private static final long serialVersionUID = 7110951353515625780L; private Long id; + /** + * The role of the author of this message. + */ @NotNull private Role role; private String name; + /** + * The contents of the message. + */ @NotEmpty private String content; + /** + * The refusal message generated by the model. + */ + private String refusal; + /** + * The tool calls generated by the model, such as function calls. + */ + @JsonProperty("tool_calls") + private List toolCalls; private Instant created; public Message() { @@ -70,6 +87,22 @@ public void setContent(final String content) { this.content = content; } + public String getRefusal() { + return refusal; + } + + public void setRefusal(String refusal) { + this.refusal = refusal; + } + + public List getToolCalls() { + return toolCalls; + } + + public void setToolCalls(List toolCalls) { + this.toolCalls = toolCalls; + } + public Instant getCreated() { return created; } diff --git a/interweb-core/src/main/java/de/l3s/interweb/core/chat/Parameters.java b/interweb-core/src/main/java/de/l3s/interweb/core/chat/Parameters.java new file mode 100644 index 00000000..d0e1045f --- /dev/null +++ b/interweb-core/src/main/java/de/l3s/interweb/core/chat/Parameters.java @@ -0,0 +1,52 @@ +package de.l3s.interweb.core.chat; + +import java.util.List; +import java.util.Map; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@RegisterForReflection +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Parameters { + + private String type = "object"; + private Map properties; + private List required; + @JsonProperty("additionalProperties") + private Boolean additionalProperties; + + public void setType(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public Map getProperties() { + return properties; + } + + public void setRequired(List required) { + this.required = required; + } + + public List getRequired() { + return required; + } + + public void setAdditionalProperties(Boolean additionalProperties) { + this.additionalProperties = additionalProperties; + } + + public Boolean getAdditionalProperties() { + return additionalProperties; + } +} diff --git a/interweb-core/src/main/java/de/l3s/interweb/core/chat/Property.java b/interweb-core/src/main/java/de/l3s/interweb/core/chat/Property.java new file mode 100644 index 00000000..7709361a --- /dev/null +++ b/interweb-core/src/main/java/de/l3s/interweb/core/chat/Property.java @@ -0,0 +1,45 @@ +package de.l3s.interweb.core.chat; + +import java.util.List; + +import jakarta.validation.constraints.NotEmpty; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@RegisterForReflection +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Property { + + @NotEmpty + private String type; + private String description; + @JsonProperty("enum") + private List enumValues; + + public void setType(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + public void setEnumValues(List enumValues) { + this.enumValues = enumValues; + } + + public List getEnumValues() { + return enumValues; + } +} diff --git a/interweb-core/src/main/java/de/l3s/interweb/core/chat/Tool.java b/interweb-core/src/main/java/de/l3s/interweb/core/chat/Tool.java new file mode 100644 index 00000000..51c3135f --- /dev/null +++ b/interweb-core/src/main/java/de/l3s/interweb/core/chat/Tool.java @@ -0,0 +1,49 @@ +package de.l3s.interweb.core.chat; + +import jakarta.validation.constraints.NotEmpty; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@RegisterForReflection +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Tool { + + /** + * The type of the tool. Currently, only `function` is supported. + */ + @NotEmpty + private String type; + + @NotEmpty + private Function function; + + public Tool() { + } + + public Tool(Function function) { + this.type = "function"; + this.function = function; + } + + public void setType(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public void setFunction(Function function) { + this.function = function; + } + + public Function getFunction() { + return function; + } + + public static FunctionToolBuilder functionBuilder() { + return new FunctionToolBuilder(); + } +} diff --git a/interweb-core/src/main/java/de/l3s/interweb/core/chat/ToolCall.java b/interweb-core/src/main/java/de/l3s/interweb/core/chat/ToolCall.java new file mode 100644 index 00000000..dce15ad0 --- /dev/null +++ b/interweb-core/src/main/java/de/l3s/interweb/core/chat/ToolCall.java @@ -0,0 +1,59 @@ +package de.l3s.interweb.core.chat; + +import java.io.Serial; +import java.io.Serializable; + +import jakarta.validation.constraints.NotEmpty; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@RegisterForReflection +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ToolCall implements Serializable { + @Serial + private static final long serialVersionUID = -1359851236515659714L; + + /** + * The id of the tool. + */ + @NotEmpty + private String id; + /** + * The type of the tool. Currently, only function is supported. + */ + @NotEmpty + private String type; + /** + * The function that the model called. + */ + @NotEmpty + @JsonProperty("function") + private CallFunction function; + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public void setType(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public void setFunction(CallFunction function) { + this.function = function; + } + + public CallFunction getFunction() { + return function; + } +} diff --git a/interweb-core/src/main/java/de/l3s/interweb/core/chat/ToolChoice.java b/interweb-core/src/main/java/de/l3s/interweb/core/chat/ToolChoice.java new file mode 100644 index 00000000..b9d9b227 --- /dev/null +++ b/interweb-core/src/main/java/de/l3s/interweb/core/chat/ToolChoice.java @@ -0,0 +1,5 @@ +package de.l3s.interweb.core.chat; + +public enum ToolChoice { + none, auto, required +}