diff --git a/core/runtime/src/main/java/dev/langchain4j/model/input/PromptTemplate.java b/core/runtime/src/main/java/dev/langchain4j/model/input/PromptTemplate.java new file mode 100644 index 000000000..2c5b6719f --- /dev/null +++ b/core/runtime/src/main/java/dev/langchain4j/model/input/PromptTemplate.java @@ -0,0 +1,160 @@ +package io.quarkiverse.langchain4j; + +import static dev.langchain4j.internal.ValidationUtils.ensureNotBlank; +import static dev.langchain4j.internal.ValidationUtils.ensureNotNull; +import static dev.langchain4j.spi.ServiceHelper.loadFactories; +import static java.util.Collections.singletonMap; + +import java.time.Clock; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.HashMap; +import java.util.Map; + +import dev.langchain4j.model.input.Prompt; +import dev.langchain4j.spi.prompt.PromptTemplateFactory; +import dev.langchain4j.spi.prompt.PromptTemplateFactory.Input; + +/** + * Represents a template of a prompt that can be reused multiple times. + * A template typically contains one or more variables (placeholders) defined as {{variable_name}} that are + * replaced with actual values to produce a Prompt. + * Special variables {{current_date}}, {{current_time}}, and {{current_date_time}} are automatically + * filled with LocalDate.now(), LocalTime.now(), and LocalDateTime.now() respectively. + */ +public class PromptTemplate { + + private static final PromptTemplateFactory FACTORY = factory(); + + private static PromptTemplateFactory factory() { + for (PromptTemplateFactory factory : loadFactories(PromptTemplateFactory.class)) { + return factory; + } + return null; // new DefaultPromptTemplateFactory(); + } + + static final String CURRENT_DATE = "current_date"; + static final String CURRENT_TIME = "current_time"; + static final String CURRENT_DATE_TIME = "current_date_time"; + + private final String templateString; + private final PromptTemplateFactory.Template template; + private final Clock clock; + + /** + * Create a new PromptTemplate. + * + *
+ * The {@code Clock} will be the system clock. + *
+ * + * @param template the template string of the prompt. + */ + public PromptTemplate(String template) { + this(template, null); + } + + /** + * Create a new PromptTemplate. + * + *+ * The {@code Clock} will be the system clock. + *
+ * + * @param template the template string of the prompt. + */ + public PromptTemplate(String template, String templateName) { + this(template, templateName, Clock.systemDefaultZone()); + } + + /** + * Create a new PromptTemplate. + * + * @param template the template string of the prompt. + * @param clock the clock to use for the special variables. + */ + PromptTemplate(String template, String templateName, Clock clock) { + this.templateString = ensureNotBlank(template, "template"); + if (templateName != null) { + this.template = FACTORY.create(new PromptTemplateFactory.Input() { + + @Override + public String getTemplate() { + return template; + } + + @Override + public String getName() { + return templateName; + } + }); + } else { + this.template = FACTORY.create(() -> template); + } + this.clock = ensureNotNull(clock, "clock"); + } + + /** + * @return A prompt template string. + */ + public String template() { + return templateString; + } + + /** + * Applies a value to a template containing a single variable. The single variable should have the name {{it}}. + * + * @param value The value that will be injected in place of the {{it}} placeholder in the template. + * @return A Prompt object where the {{it}} placeholder in the template has been replaced by the provided value. + */ + public Prompt apply(Object value) { + return apply(singletonMap("it", value)); + } + + /** + * Applies multiple values to a template containing multiple variables. + * + * @param variables A map of variable names to values that will be injected in place of the corresponding placeholders in + * the template. + * @return A Prompt object where the placeholders in the template have been replaced by the provided values. + */ + public Prompt apply(Map+ * These URIs have the format: + * + *
+ * qute-java://<fully-qualified-class-name>[#method][@annotation] + *+ * + * Examples: + *
qute-java://com.acme.Bean@io.quarkus.qute.TemplateContentsqute-java://com.acme.Bean#process@io.quarkus.qute.TemplateContents+ * This builder is used to construct such URIs in a type-safe way and to provide + * utility methods to identify and parse them. It is aligned with + * {@link io.quarkus.qute.debug.client.JavaSourceLocationArguments#javaElementUri}. + *
+ */ +public class JavaElementUriBuilder { + + /** Scheme used for Qute Java URIs. */ + public static final String QUTE_JAVA_SCHEME = "qute-java"; + + /** Prefix for Qute Java URIs. */ + public static final String QUTE_JAVA_URI_PREFIX = QUTE_JAVA_SCHEME + "://"; + + private final String typeName; + private String method; + private String annotation; + + private JavaElementUriBuilder(String typeName) { + this.typeName = typeName; + } + + /** + * Returns the fully qualified Java class name for this URI. + * + * @return the class name + */ + public String getTypeName() { + return typeName; + } + + /** + * Returns the Java method name (nullable if not specified). + * + * @return the method name or {@code null} + */ + public String getMethod() { + return method; + } + + /** + * Sets the Java method name. + * + * @param method the method name to set + * @return this builder + */ + public JavaElementUriBuilder setMethod(String method) { + this.method = method; + return this; + } + + /** + * Returns the fully qualified Java annotation name (nullable if not specified). + * + * @return the annotation name or {@code null} + */ + public String getAnnotation() { + return annotation; + } + + /** + * Sets the fully qualified Java annotation name. + * + * @param annotation the annotation name to set + * @return this builder + */ + public JavaElementUriBuilder setAnnotation(String annotation) { + this.annotation = annotation; + return this; + } + + /** + * Creates a new builder for the given Java class name. + * + * @param typeName fully qualified Java class name + * @return a new {@link JavaElementUriBuilder} + */ + public static JavaElementUriBuilder builder(String typeName) { + return new JavaElementUriBuilder(typeName); + } + + /** + * Builds the Qute Java URI representing the element. + * + * @return a {@link URI} for the Java element + */ + public URI build() { + StringBuilder uri = new StringBuilder(QUTE_JAVA_URI_PREFIX); + uri.append(typeName); + if (method != null) { + uri.append("#").append(method); + } + if (annotation != null) { + uri.append("@").append(annotation); + } + return URI.create(uri.toString()); + } + + /** + * Returns true if the given URI uses the qute-java scheme. + * + * @param uri the URI to check + * @return {@code true} if this URI is a Qute Java element URI + */ + public static boolean isJavaUri(URI uri) { + return uri != null && QUTE_JAVA_SCHEME.equals(uri.getScheme()); + } + + /** + * Returns true if the given string starts with the qute-java URI prefix. + * + * @param uri the URI string to check + * @return {@code true} if this string is a Qute Java element URI + */ + public static boolean isJavaUri(String uri) { + return uri != null && uri.startsWith(QUTE_JAVA_URI_PREFIX); + } +} diff --git a/core/runtime/src/main/java/io/quarkiverse/langchain4j/runtime/aiservice/AiServiceMethodImplementationSupport.java b/core/runtime/src/main/java/io/quarkiverse/langchain4j/runtime/aiservice/AiServiceMethodImplementationSupport.java index d751628c3..db4adbb99 100644 --- a/core/runtime/src/main/java/io/quarkiverse/langchain4j/runtime/aiservice/AiServiceMethodImplementationSupport.java +++ b/core/runtime/src/main/java/io/quarkiverse/langchain4j/runtime/aiservice/AiServiceMethodImplementationSupport.java @@ -68,6 +68,7 @@ import dev.langchain4j.model.chat.response.ChatResponse; import dev.langchain4j.model.input.Prompt; import dev.langchain4j.model.input.PromptTemplate; +//import dev.langchain4j.model.input.PromptTemplate; import dev.langchain4j.model.input.structured.StructuredPrompt; import dev.langchain4j.model.input.structured.StructuredPromptProcessor; import dev.langchain4j.model.moderation.Moderation; @@ -100,6 +101,7 @@ import io.quarkiverse.langchain4j.VideoUrl; import io.quarkiverse.langchain4j.response.ResponseAugmenterParams; import io.quarkiverse.langchain4j.runtime.ContextLocals; +import io.quarkiverse.langchain4j.runtime.JavaElementUriBuilder; import io.quarkiverse.langchain4j.runtime.QuarkusServiceOutputParser; import io.quarkiverse.langchain4j.runtime.ResponseSchemaUtil; import io.quarkiverse.langchain4j.runtime.aiservice.GuardrailsSupport.OutputGuardrailStreamingMapper; @@ -887,12 +889,23 @@ private static Optional