001/*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2025-2026 The Enola <https://enola.dev> Authors
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *     https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package dev.enola.ai.dotprompt;
019
020import dev.enola.common.io.object.WithSchema;
021import dev.enola.common.template.Template;
022
023import org.jspecify.annotations.Nullable;
024
025import java.net.URI;
026import java.util.HashMap;
027import java.util.HashSet;
028import java.util.Map;
029import java.util.Set;
030
031/**
032 * Dot Prompt struct, see <a
033 * href="https://google.github.io/dotprompt/reference/frontmatter/">Spec</a> and the (TypeScript) <a
034 * href="https://github.com/google/dotprompt/blob/main/js/src/types.ts">Reference
035 * Implementation</a>.
036 *
037 * @author <a href="http://www.vorburger.ch">Michael Vorburger.ch</a>
038 */
039public class DotPrompt extends WithSchema {
040
041    // TODO Rethink @Nullable ... it's a mess because some fields MAY be null
042    //   initially, e.g. in a YAML frontmatter, but then WONT be null after DotPromptLoader;
043    //   should there be 2 different (sub?)classes - just because of that?!
044
045    // This is a class instead of a record to allow users to extend it.
046
047    // This is a trivial "struct" instead of a (*Builder) "bean", with getters and setters,
048    // because... there is really no need for that, here - this is totally fine and enough.
049
050    /** The URL of where this DotPrompt originated. */
051    public @Nullable URI id;
052
053    /**
054     * The name of the prompt. If not specified, it will be inferred from the filename in the URL of
055     * the loaded prompt (e.g. {@code http://example.org/stuff/example.prompt.md} has an inferred
056     * name of {@code example}).
057     */
058    public @Nullable String name;
059
060    /**
061     * The variant name for the prompt. If null, then inferred from the filename in the URL of the
062     * loaded prompt (e.g. {@code http://example.org/stuff/example.variant1.prompt.md} has an
063     * inferred name of {@code example} and inferred variant of {@code variant1}).
064     */
065    // TODO public @Nullable String variant;
066
067    /**
068     * The name of the model to use for this prompt, based on the <a
069     * href="https://docs.enola.dev/specs/aiuri/">Enola.dev AI URI specification</a>; so e.g.,
070     * {@code google://?model=gemini-2.5-flash}. May be omitted, in which case a default model will
071     * be used.
072     */
073    public @Nullable String model;
074
075    /**
076     * Configuration to be passed to the model. The specific options may vary depending on the
077     * model. Note that version, temperature, topK, topP & maxOutputTokens are already specified as
078     * query parameters in the model AI URI instead of here.
079     */
080    // TODO public final Map<String, Object> config = new HashMap<>();
081
082    /** Names of registered tools to allow use of in this prompt. */
083    public final Set<String> tools = new HashSet<>();
084
085    /** Arbitrary metadata to be used by code, tools, and libraries. */
086    // TODO public final Map<String, Object> metadata = new HashMap<>();
087
088    /** Defines the (schema of the) input variables the prompt. */
089    public @Nullable Input input;
090
091    /** Defines the expected model output format. */
092    public @Nullable Output output;
093
094    /**
095     * Template of Prompt, as text.
096     *
097     * <p>This is typically in the body (after the YAML frontmatter) of a .prompt file.
098     */
099    public @Nullable String prompt;
100
101    /** Template of Prompt, as Template (from parsed {@link #prompt}). */
102    public @Nullable Template template;
103
104    // TODO How to class input Map<String, Object> extends Map<String, Object> ?!
105    public static class Input {
106
107        /**
108         * Defines the default input variable values to use if none are provided. Input values
109         * passed from the implementation should be merged into these values with a shallow merge
110         * strategy.
111         */
112        // TODO @JsonProperty("default") // cauz "default" is a reserved keyword
113        // TODO https://github.com/google/dotprompt/issues/306 re. Map<String, Object>
114        // TODO public final Map<String, Object> defaults = new HashMap<>();
115
116        /**
117         * Schema representing the input values for the prompt. Must correspond to a JSON Schema
118         * {@code object} type.
119         */
120        // TODO Validate by parsing with JSON Schema parser, not (just) text to basic JSON Map
121        public final Map<String, Object> schema = new HashMap<>();
122
123        // TODO public @Nullable URI schemaRef;
124    }
125
126    public static class Output {
127
128        public enum Format {
129            json,
130            text
131        }
132
133        /**
134         * Desired output format for this prompt. Implicitly set to JSON if the schema is specified.
135         */
136        // TODO Remove? ADK doesn't seem to support this; it just has LlmAgent.outputSchema(Schema)
137        public Format format = Format.text;
138
139        /**
140         * Schema representing the expected output from the prompt. Must correspond to a JSON Schema
141         * {@code object} type.
142         */
143        // TODO Validate by parsing with JSON Schema parser, not (just) text to basic JSON Map
144        public final Map<String, Object> schema = new HashMap<>();
145
146        // TODO public @Nullable URI schemaRef;
147
148        // TODO public @Nullable String outputKey;
149    }
150}