From ec420d475fca652f233ae4aec67974d7d799f33f Mon Sep 17 00:00:00 2001 From: xavidop Date: Mon, 30 Mar 2026 01:12:21 +0200 Subject: [PATCH 01/50] feat: middlewareV2 --- .../com/google/genkit/ai/GenerateOptions.java | 42 +- .../middleware/BaseGenerationMiddleware.java | 78 ++ .../genkit/ai/middleware/GenerateNext.java | 38 + .../genkit/ai/middleware/GenerateParams.java | 54 ++ .../ai/middleware/GenerationMiddleware.java | 126 ++++ .../genkit/ai/middleware/ModelNext.java | 38 + .../genkit/ai/middleware/ModelParams.java | 56 ++ .../google/genkit/ai/middleware/ToolNext.java | 38 + .../genkit/ai/middleware/ToolParams.java | 50 ++ .../middleware/GenerationMiddlewareTest.java | 664 ++++++++++++++++++ .../genkit/core/middleware/Middleware.java | 3 + .../main/java/com/google/genkit/Genkit.java | 295 ++++++-- pom.xml | 1 + samples/middleware-v2/README.md | 161 +++++ samples/middleware-v2/pom.xml | 91 +++ samples/middleware-v2/run.sh | 34 + .../genkit/samples/MiddlewareV2Sample.java | 402 +++++++++++ .../src/main/resources/logback.xml | 26 + 18 files changed, 2139 insertions(+), 58 deletions(-) create mode 100644 ai/src/main/java/com/google/genkit/ai/middleware/BaseGenerationMiddleware.java create mode 100644 ai/src/main/java/com/google/genkit/ai/middleware/GenerateNext.java create mode 100644 ai/src/main/java/com/google/genkit/ai/middleware/GenerateParams.java create mode 100644 ai/src/main/java/com/google/genkit/ai/middleware/GenerationMiddleware.java create mode 100644 ai/src/main/java/com/google/genkit/ai/middleware/ModelNext.java create mode 100644 ai/src/main/java/com/google/genkit/ai/middleware/ModelParams.java create mode 100644 ai/src/main/java/com/google/genkit/ai/middleware/ToolNext.java create mode 100644 ai/src/main/java/com/google/genkit/ai/middleware/ToolParams.java create mode 100644 ai/src/test/java/com/google/genkit/ai/middleware/GenerationMiddlewareTest.java create mode 100644 samples/middleware-v2/README.md create mode 100644 samples/middleware-v2/pom.xml create mode 100755 samples/middleware-v2/run.sh create mode 100644 samples/middleware-v2/src/main/java/com/google/genkit/samples/MiddlewareV2Sample.java create mode 100644 samples/middleware-v2/src/main/resources/logback.xml diff --git a/ai/src/main/java/com/google/genkit/ai/GenerateOptions.java b/ai/src/main/java/com/google/genkit/ai/GenerateOptions.java index 3017243b5..ad3a38571 100644 --- a/ai/src/main/java/com/google/genkit/ai/GenerateOptions.java +++ b/ai/src/main/java/com/google/genkit/ai/GenerateOptions.java @@ -18,6 +18,7 @@ package com.google.genkit.ai; +import com.google.genkit.ai.middleware.GenerationMiddleware; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -43,6 +44,7 @@ public class GenerateOptions { private final Integer maxTurns; private final ResumeOptions resume; private final Class outputClass; + private final List use; /** * Creates new GenerateOptions. @@ -74,7 +76,8 @@ public GenerateOptions( Map context, Integer maxTurns, ResumeOptions resume, - Class outputClass) { + Class outputClass, + List use) { this.model = model; this.prompt = prompt; this.messages = messages; @@ -88,6 +91,7 @@ public GenerateOptions( this.maxTurns = maxTurns; this.resume = resume; this.outputClass = outputClass; + this.use = use; } /** @@ -286,6 +290,15 @@ public Class getOutputClass() { return outputClass; } + /** + * Gets the V2 middleware to apply to this generation. + * + * @return the middleware list, or null if not set + */ + public List getUse() { + return use; + } + /** * Builder for GenerateOptions. * @@ -305,6 +318,7 @@ public static class Builder { private Integer maxTurns; private ResumeOptions resume; private Class outputClass; + private List use; public Builder model(String model) { this.model = model; @@ -407,6 +421,29 @@ public Builder resume(ResumeOptions resume) { return this; } + /** + * Sets V2 middleware to apply to this generation. Middleware hooks wrap the generate loop, + * model calls, and tool executions. + * + * @param use the middleware to apply + * @return this builder + */ + public Builder use(List use) { + this.use = use; + return this; + } + + /** + * Sets V2 middleware to apply to this generation. + * + * @param middleware the middleware to apply + * @return this builder + */ + public Builder use(GenerationMiddleware... middleware) { + this.use = List.of(middleware); + return this; + } + public GenerateOptions build() { return new GenerateOptions<>( model, @@ -421,7 +458,8 @@ public GenerateOptions build() { context, maxTurns, resume, - outputClass); + outputClass, + use); } } } diff --git a/ai/src/main/java/com/google/genkit/ai/middleware/BaseGenerationMiddleware.java b/ai/src/main/java/com/google/genkit/ai/middleware/BaseGenerationMiddleware.java new file mode 100644 index 000000000..d327ea579 --- /dev/null +++ b/ai/src/main/java/com/google/genkit/ai/middleware/BaseGenerationMiddleware.java @@ -0,0 +1,78 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.genkit.ai.middleware; + +import com.google.genkit.ai.ModelResponse; +import com.google.genkit.ai.Tool; +import com.google.genkit.ai.ToolResponse; +import com.google.genkit.core.ActionContext; +import com.google.genkit.core.GenkitException; +import java.util.Collections; +import java.util.List; + +/** + * BaseGenerationMiddleware provides default pass-through implementations for all three hooks. + * Extend this class and override only the hooks you need. + * + *

Example: + * + *

{@code
+ * public class TimingMiddleware extends BaseGenerationMiddleware {
+ *   @Override
+ *   public String name() { return "timing"; }
+ *
+ *   @Override
+ *   public GenerationMiddleware newInstance() { return new TimingMiddleware(); }
+ *
+ *   @Override
+ *   public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next)
+ *       throws GenkitException {
+ *     long start = System.currentTimeMillis();
+ *     ModelResponse resp = next.apply(ctx, params);
+ *     System.out.println("Model call took " + (System.currentTimeMillis() - start) + "ms");
+ *     return resp;
+ *   }
+ * }
+ * }
+ */ +public abstract class BaseGenerationMiddleware implements GenerationMiddleware { + + @Override + public ModelResponse wrapGenerate(ActionContext ctx, GenerateParams params, GenerateNext next) + throws GenkitException { + return next.apply(ctx, params); + } + + @Override + public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next) + throws GenkitException { + return next.apply(ctx, params); + } + + @Override + public ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next) + throws GenkitException { + return next.apply(ctx, params); + } + + @Override + public List> tools() { + return Collections.emptyList(); + } +} diff --git a/ai/src/main/java/com/google/genkit/ai/middleware/GenerateNext.java b/ai/src/main/java/com/google/genkit/ai/middleware/GenerateNext.java new file mode 100644 index 000000000..5301e8d50 --- /dev/null +++ b/ai/src/main/java/com/google/genkit/ai/middleware/GenerateNext.java @@ -0,0 +1,38 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.genkit.ai.middleware; + +import com.google.genkit.ai.ModelResponse; +import com.google.genkit.core.ActionContext; +import com.google.genkit.core.GenkitException; + +/** Next function in the {@link GenerationMiddleware#wrapGenerate} hook chain. */ +@FunctionalInterface +public interface GenerateNext { + + /** + * Calls the next handler in the generate chain. + * + * @param ctx the action context + * @param params the generate parameters + * @return the model response + * @throws GenkitException if processing fails + */ + ModelResponse apply(ActionContext ctx, GenerateParams params) throws GenkitException; +} diff --git a/ai/src/main/java/com/google/genkit/ai/middleware/GenerateParams.java b/ai/src/main/java/com/google/genkit/ai/middleware/GenerateParams.java new file mode 100644 index 000000000..7508ec467 --- /dev/null +++ b/ai/src/main/java/com/google/genkit/ai/middleware/GenerateParams.java @@ -0,0 +1,54 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.genkit.ai.middleware; + +import com.google.genkit.ai.ModelRequest; + +/** Holds parameters for the {@link GenerationMiddleware#wrapGenerate} hook. */ +public class GenerateParams { + + private final ModelRequest request; + private final int iteration; + + /** + * Creates GenerateParams. + * + * @param request the current model request for this iteration + * @param iteration the current tool-loop iteration (0-indexed) + */ + public GenerateParams(ModelRequest request, int iteration) { + this.request = request; + this.iteration = iteration; + } + + /** Returns the current model request with accumulated messages. */ + public ModelRequest getRequest() { + return request; + } + + /** Returns the current tool-loop iteration (0-indexed). */ + public int getIteration() { + return iteration; + } + + /** Returns a new GenerateParams with the given request, preserving the iteration. */ + public GenerateParams withRequest(ModelRequest request) { + return new GenerateParams(request, this.iteration); + } +} diff --git a/ai/src/main/java/com/google/genkit/ai/middleware/GenerationMiddleware.java b/ai/src/main/java/com/google/genkit/ai/middleware/GenerationMiddleware.java new file mode 100644 index 000000000..27db05f05 --- /dev/null +++ b/ai/src/main/java/com/google/genkit/ai/middleware/GenerationMiddleware.java @@ -0,0 +1,126 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.genkit.ai.middleware; + +import com.google.genkit.ai.ModelResponse; +import com.google.genkit.ai.Tool; +import com.google.genkit.ai.ToolResponse; +import com.google.genkit.core.ActionContext; +import com.google.genkit.core.GenkitException; +import java.util.Collections; +import java.util.List; + +/** + * GenerationMiddleware provides hooks for different stages of the generation pipeline. + * + *

This is the V2 middleware interface that replaces the generic {@code Middleware}. It + * provides three distinct hooks: + * + *

    + *
  • {@link #wrapGenerate} - wraps each iteration of the tool loop + *
  • {@link #wrapModel} - wraps each model API call + *
  • {@link #wrapTool} - wraps each tool execution + *
+ * + *

Each {@code generate()} call creates a fresh instance via {@link #newInstance()}, enabling + * per-invocation state (e.g., counters, timers) without shared mutable state across requests. + * + *

Example: + * + *

{@code
+ * public class LoggingMiddleware extends BaseGenerationMiddleware {
+ *   private int modelCalls = 0;
+ *
+ *   @Override
+ *   public String name() { return "logging"; }
+ *
+ *   @Override
+ *   public GenerationMiddleware newInstance() { return new LoggingMiddleware(); }
+ *
+ *   @Override
+ *   public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next)
+ *       throws GenkitException {
+ *     modelCalls++;
+ *     System.out.println("Model call #" + modelCalls);
+ *     ModelResponse resp = next.apply(ctx, params);
+ *     System.out.println("Model responded with " + resp.getText());
+ *     return resp;
+ *   }
+ * }
+ * }
+ */ +public interface GenerationMiddleware { + + /** Returns the middleware's unique identifier. */ + String name(); + + /** + * Returns a fresh instance for each {@code generate()} call, enabling per-invocation state. + * + *

Stable state (e.g., API keys, configuration) should be preserved. Per-request state (e.g., + * counters) should be reset. + */ + GenerationMiddleware newInstance(); + + /** + * Wraps each iteration of the generate tool loop. + * + * @param ctx the action context + * @param params the generate parameters including the current request and iteration + * @param next the next function in the chain + * @return the model response + * @throws GenkitException if processing fails + */ + ModelResponse wrapGenerate(ActionContext ctx, GenerateParams params, GenerateNext next) + throws GenkitException; + + /** + * Wraps each model API call. + * + * @param ctx the action context + * @param params the model parameters including the request + * @param next the next function in the chain + * @return the model response + * @throws GenkitException if processing fails + */ + ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next) + throws GenkitException; + + /** + * Wraps each tool execution. May be called concurrently when multiple tools execute in parallel. + * Implementations must be safe for concurrent use. + * + * @param ctx the action context + * @param params the tool parameters including the request and resolved tool + * @param next the next function in the chain + * @return the tool response + * @throws GenkitException if processing fails + */ + ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next) throws GenkitException; + + /** + * Returns additional tools to make available during generation. These tools are dynamically added + * when the middleware is used. + * + * @return the list of additional tools, or empty list if none + */ + default List> tools() { + return Collections.emptyList(); + } +} diff --git a/ai/src/main/java/com/google/genkit/ai/middleware/ModelNext.java b/ai/src/main/java/com/google/genkit/ai/middleware/ModelNext.java new file mode 100644 index 000000000..227b87f56 --- /dev/null +++ b/ai/src/main/java/com/google/genkit/ai/middleware/ModelNext.java @@ -0,0 +1,38 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.genkit.ai.middleware; + +import com.google.genkit.ai.ModelResponse; +import com.google.genkit.core.ActionContext; +import com.google.genkit.core.GenkitException; + +/** Next function in the {@link GenerationMiddleware#wrapModel} hook chain. */ +@FunctionalInterface +public interface ModelNext { + + /** + * Calls the next handler in the model chain. + * + * @param ctx the action context + * @param params the model parameters + * @return the model response + * @throws GenkitException if processing fails + */ + ModelResponse apply(ActionContext ctx, ModelParams params) throws GenkitException; +} diff --git a/ai/src/main/java/com/google/genkit/ai/middleware/ModelParams.java b/ai/src/main/java/com/google/genkit/ai/middleware/ModelParams.java new file mode 100644 index 000000000..0e2aa1633 --- /dev/null +++ b/ai/src/main/java/com/google/genkit/ai/middleware/ModelParams.java @@ -0,0 +1,56 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.genkit.ai.middleware; + +import com.google.genkit.ai.ModelRequest; +import com.google.genkit.ai.ModelResponseChunk; +import java.util.function.Consumer; + +/** Holds parameters for the {@link GenerationMiddleware#wrapModel} hook. */ +public class ModelParams { + + private final ModelRequest request; + private final Consumer streamCallback; + + /** + * Creates ModelParams. + * + * @param request the model request about to be sent + * @param streamCallback the streaming callback, or null if not streaming + */ + public ModelParams(ModelRequest request, Consumer streamCallback) { + this.request = request; + this.streamCallback = streamCallback; + } + + /** Returns the model request about to be sent. */ + public ModelRequest getRequest() { + return request; + } + + /** Returns the streaming callback, or null if not streaming. */ + public Consumer getStreamCallback() { + return streamCallback; + } + + /** Returns a new ModelParams with the given request, preserving the stream callback. */ + public ModelParams withRequest(ModelRequest request) { + return new ModelParams(request, this.streamCallback); + } +} diff --git a/ai/src/main/java/com/google/genkit/ai/middleware/ToolNext.java b/ai/src/main/java/com/google/genkit/ai/middleware/ToolNext.java new file mode 100644 index 000000000..53d79e835 --- /dev/null +++ b/ai/src/main/java/com/google/genkit/ai/middleware/ToolNext.java @@ -0,0 +1,38 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.genkit.ai.middleware; + +import com.google.genkit.ai.ToolResponse; +import com.google.genkit.core.ActionContext; +import com.google.genkit.core.GenkitException; + +/** Next function in the {@link GenerationMiddleware#wrapTool} hook chain. */ +@FunctionalInterface +public interface ToolNext { + + /** + * Calls the next handler in the tool chain. + * + * @param ctx the action context + * @param params the tool parameters + * @return the tool response + * @throws GenkitException if processing fails + */ + ToolResponse apply(ActionContext ctx, ToolParams params) throws GenkitException; +} diff --git a/ai/src/main/java/com/google/genkit/ai/middleware/ToolParams.java b/ai/src/main/java/com/google/genkit/ai/middleware/ToolParams.java new file mode 100644 index 000000000..36ba87732 --- /dev/null +++ b/ai/src/main/java/com/google/genkit/ai/middleware/ToolParams.java @@ -0,0 +1,50 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.genkit.ai.middleware; + +import com.google.genkit.ai.Tool; +import com.google.genkit.ai.ToolRequest; + +/** Holds parameters for the {@link GenerationMiddleware#wrapTool} hook. */ +public class ToolParams { + + private final ToolRequest request; + private final Tool tool; + + /** + * Creates ToolParams. + * + * @param request the tool request about to be executed + * @param tool the resolved tool being called + */ + public ToolParams(ToolRequest request, Tool tool) { + this.request = request; + this.tool = tool; + } + + /** Returns the tool request about to be executed. */ + public ToolRequest getRequest() { + return request; + } + + /** Returns the resolved tool being called. */ + public Tool getTool() { + return tool; + } +} diff --git a/ai/src/test/java/com/google/genkit/ai/middleware/GenerationMiddlewareTest.java b/ai/src/test/java/com/google/genkit/ai/middleware/GenerationMiddlewareTest.java new file mode 100644 index 000000000..0b40f28c8 --- /dev/null +++ b/ai/src/test/java/com/google/genkit/ai/middleware/GenerationMiddlewareTest.java @@ -0,0 +1,664 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.genkit.ai.middleware; + +import static org.junit.jupiter.api.Assertions.*; + +import com.google.genkit.ai.Candidate; +import com.google.genkit.ai.Message; +import com.google.genkit.ai.ModelRequest; +import com.google.genkit.ai.ModelResponse; +import com.google.genkit.ai.Tool; +import com.google.genkit.ai.ToolRequest; +import com.google.genkit.ai.ToolResponse; +import com.google.genkit.core.ActionContext; +import com.google.genkit.core.DefaultRegistry; +import com.google.genkit.core.GenkitException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** Tests for V2 GenerationMiddleware hooks: GenerateNext, ModelNext, ToolNext. */ +class GenerationMiddlewareTest { + + private ActionContext ctx; + + @BeforeEach + void setUp() { + ctx = new ActionContext(new DefaultRegistry()); + } + + // ========================================================================= + // Helper: build a simple ModelResponse with text + // ========================================================================= + + private static ModelResponse responseWithText(String text) { + Message msg = Message.model(text); + Candidate candidate = new Candidate(msg); + return ModelResponse.builder().addCandidate(candidate).build(); + } + + // ========================================================================= + // GenerateNext tests + // ========================================================================= + + @Test + void testGenerateNext_passThrough() { + ModelRequest request = ModelRequest.builder().addUserMessage("hello").build(); + GenerateParams params = new GenerateParams(request, 0); + ModelResponse expected = responseWithText("world"); + + GenerateNext next = (c, p) -> expected; + + ModelResponse result = next.apply(ctx, params); + assertSame(expected, result); + } + + @Test + void testGenerateNext_chainOrder() { + List order = new ArrayList<>(); + + // Core function + GenerateNext core = + (c, p) -> { + order.add("core"); + return responseWithText("response"); + }; + + // Outer middleware wrapping core + GenerateNext outer = + (c, p) -> { + order.add("outer-before"); + ModelResponse resp = core.apply(c, p); + order.add("outer-after"); + return resp; + }; + + ModelRequest request = ModelRequest.builder().addUserMessage("test").build(); + outer.apply(ctx, new GenerateParams(request, 0)); + + assertEquals(List.of("outer-before", "core", "outer-after"), order); + } + + @Test + void testGenerateNext_canModifyParams() { + ModelRequest original = ModelRequest.builder().addUserMessage("original").build(); + ModelRequest modified = ModelRequest.builder().addUserMessage("modified").build(); + + AtomicInteger iterationSeen = new AtomicInteger(-1); + GenerateNext core = + (c, p) -> { + iterationSeen.set(p.getIteration()); + assertEquals(modified, p.getRequest()); + return responseWithText("ok"); + }; + + // Middleware that replaces the request + GenerateNext wrapper = + (c, p) -> { + GenerateParams newParams = p.withRequest(modified); + return core.apply(c, newParams); + }; + + wrapper.apply(ctx, new GenerateParams(original, 5)); + assertEquals(5, iterationSeen.get()); // iteration preserved by withRequest + } + + @Test + void testGenerateNext_exceptionPropagates() { + GenerateNext failing = + (c, p) -> { + throw new GenkitException("boom"); + }; + + ModelRequest request = ModelRequest.builder().build(); + assertThrows(GenkitException.class, () -> failing.apply(ctx, new GenerateParams(request, 0))); + } + + // ========================================================================= + // ModelNext tests + // ========================================================================= + + @Test + void testModelNext_passThrough() { + ModelRequest request = ModelRequest.builder().addUserMessage("hello").build(); + ModelParams params = new ModelParams(request, null); + ModelResponse expected = responseWithText("model output"); + + ModelNext next = (c, p) -> expected; + + ModelResponse result = next.apply(ctx, params); + assertSame(expected, result); + } + + @Test + void testModelNext_chainOrder() { + List order = new ArrayList<>(); + + ModelNext core = + (c, p) -> { + order.add("model"); + return responseWithText("result"); + }; + + ModelNext wrapper = + (c, p) -> { + order.add("before-model"); + ModelResponse resp = core.apply(c, p); + order.add("after-model"); + return resp; + }; + + ModelRequest request = ModelRequest.builder().build(); + wrapper.apply(ctx, new ModelParams(request, null)); + + assertEquals(List.of("before-model", "model", "after-model"), order); + } + + @Test + void testModelNext_canModifyRequest() { + ModelRequest original = ModelRequest.builder().addUserMessage("original").build(); + ModelRequest modified = ModelRequest.builder().addUserMessage("injected").build(); + + ModelNext core = + (c, p) -> { + assertEquals(modified, p.getRequest()); + return responseWithText("ok"); + }; + + ModelNext wrapper = + (c, p) -> { + ModelParams newParams = p.withRequest(modified); + return core.apply(c, newParams); + }; + + wrapper.apply(ctx, new ModelParams(original, null)); + } + + @Test + void testModelNext_preservesStreamCallback() { + List streamed = new ArrayList<>(); + ModelParams params = + new ModelParams(ModelRequest.builder().build(), chunk -> streamed.add("chunk")); + + ModelNext next = + (c, p) -> { + assertNotNull(p.getStreamCallback()); + return responseWithText("ok"); + }; + + next.apply(ctx, params); + assertNotNull(params.getStreamCallback()); + } + + @Test + void testModelNext_exceptionPropagates() { + ModelNext failing = + (c, p) -> { + throw new GenkitException("model failed"); + }; + + assertThrows( + GenkitException.class, + () -> failing.apply(ctx, new ModelParams(ModelRequest.builder().build(), null))); + } + + // ========================================================================= + // ToolNext tests + // ========================================================================= + + @Test + void testToolNext_passThrough() { + ToolRequest toolReq = new ToolRequest("myTool", Map.of("key", "value")); + Tool tool = createTestTool("myTool"); + ToolParams params = new ToolParams(toolReq, tool); + ToolResponse expected = new ToolResponse("myTool", "tool output"); + + ToolNext next = (c, p) -> expected; + + ToolResponse result = next.apply(ctx, params); + assertSame(expected, result); + } + + @Test + void testToolNext_chainOrder() { + List order = new ArrayList<>(); + + ToolNext core = + (c, p) -> { + order.add("tool-exec"); + return new ToolResponse(p.getRequest().getName(), "result"); + }; + + ToolNext wrapper = + (c, p) -> { + order.add("before-tool"); + ToolResponse resp = core.apply(c, p); + order.add("after-tool"); + return resp; + }; + + ToolRequest toolReq = new ToolRequest("test", Map.of()); + wrapper.apply(ctx, new ToolParams(toolReq, createTestTool("test"))); + + assertEquals(List.of("before-tool", "tool-exec", "after-tool"), order); + } + + @Test + void testToolNext_accessesToolInfo() { + Tool tool = createTestTool("weatherTool"); + ToolRequest toolReq = new ToolRequest("weatherTool", Map.of("city", "Paris")); + + ToolNext next = + (c, p) -> { + assertEquals("weatherTool", p.getRequest().getName()); + assertEquals("weatherTool", p.getTool().getName()); + return new ToolResponse("weatherTool", "sunny"); + }; + + ToolResponse resp = next.apply(ctx, new ToolParams(toolReq, tool)); + assertEquals("weatherTool", resp.getName()); + } + + @Test + void testToolNext_exceptionPropagates() { + ToolNext failing = + (c, p) -> { + throw new GenkitException("tool failed"); + }; + + ToolRequest toolReq = new ToolRequest("t", Map.of()); + assertThrows( + GenkitException.class, + () -> failing.apply(ctx, new ToolParams(toolReq, createTestTool("t")))); + } + + // ========================================================================= + // BaseGenerationMiddleware tests + // ========================================================================= + + @Test + void testBaseMiddleware_defaultsPassThrough() { + BaseGenerationMiddleware base = + new BaseGenerationMiddleware() { + @Override + public String name() { + return "noop"; + } + + @Override + public GenerationMiddleware newInstance() { + return this; + } + }; + + // wrapGenerate passes through + ModelRequest req = ModelRequest.builder().addUserMessage("test").build(); + ModelResponse expected = responseWithText("pass"); + GenerateNext gNext = (c, p) -> expected; + ModelResponse gResult = base.wrapGenerate(ctx, new GenerateParams(req, 0), gNext); + assertSame(expected, gResult); + + // wrapModel passes through + ModelNext mNext = (c, p) -> expected; + ModelResponse mResult = base.wrapModel(ctx, new ModelParams(req, null), mNext); + assertSame(expected, mResult); + + // wrapTool passes through + ToolResponse toolExpected = new ToolResponse("t", "data"); + ToolNext tNext = (c, p) -> toolExpected; + ToolResponse tResult = + base.wrapTool( + ctx, new ToolParams(new ToolRequest("t", Map.of()), createTestTool("t")), tNext); + assertSame(toolExpected, tResult); + + // tools returns empty + assertTrue(base.tools().isEmpty()); + } + + @Test + void testCustomMiddleware_overridesSelectedHooks() { + AtomicInteger modelCallCount = new AtomicInteger(0); + + BaseGenerationMiddleware middleware = + new BaseGenerationMiddleware() { + @Override + public String name() { + return "model-counter"; + } + + @Override + public GenerationMiddleware newInstance() { + return this; + } + + @Override + public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next) + throws GenkitException { + modelCallCount.incrementAndGet(); + return next.apply(ctx, params); + } + }; + + ModelRequest req = ModelRequest.builder().build(); + ModelResponse resp = responseWithText("ok"); + + // wrapModel is overridden + middleware.wrapModel(ctx, new ModelParams(req, null), (c, p) -> resp); + assertEquals(1, modelCallCount.get()); + + // wrapGenerate still passes through (default) + ModelResponse gResp = middleware.wrapGenerate(ctx, new GenerateParams(req, 0), (c, p) -> resp); + assertSame(resp, gResp); + assertEquals(1, modelCallCount.get()); // not incremented + } + + // ========================================================================= + // Chaining multiple middleware + // ========================================================================= + + @Test + void testChainGenerateHooks_nestedOrder() { + List order = new ArrayList<>(); + + GenerationMiddleware outer = + new BaseGenerationMiddleware() { + @Override + public String name() { + return "outer"; + } + + @Override + public GenerationMiddleware newInstance() { + return this; + } + + @Override + public ModelResponse wrapGenerate( + ActionContext ctx, GenerateParams params, GenerateNext next) throws GenkitException { + order.add("outer-before"); + ModelResponse resp = next.apply(ctx, params); + order.add("outer-after"); + return resp; + } + }; + + GenerationMiddleware inner = + new BaseGenerationMiddleware() { + @Override + public String name() { + return "inner"; + } + + @Override + public GenerationMiddleware newInstance() { + return this; + } + + @Override + public ModelResponse wrapGenerate( + ActionContext ctx, GenerateParams params, GenerateNext next) throws GenkitException { + order.add("inner-before"); + ModelResponse resp = next.apply(ctx, params); + order.add("inner-after"); + return resp; + } + }; + + // Chain: outer wraps inner wraps core + // This mirrors the chaining in Genkit.chainGenerateHooks() + List middlewares = List.of(outer, inner); + GenerateNext core = + (c, p) -> { + order.add("core"); + return responseWithText("done"); + }; + + // Build chain by reverse iteration (first middleware = outermost) + GenerateNext chain = core; + for (int i = middlewares.size() - 1; i >= 0; i--) { + GenerationMiddleware mw = middlewares.get(i); + GenerateNext wrapped = chain; + chain = (c, p) -> mw.wrapGenerate(c, p, wrapped); + } + + ModelRequest req = ModelRequest.builder().build(); + chain.apply(ctx, new GenerateParams(req, 0)); + + assertEquals( + List.of("outer-before", "inner-before", "core", "inner-after", "outer-after"), order); + } + + @Test + void testChainModelHooks_nestedOrder() { + List order = new ArrayList<>(); + + GenerationMiddleware first = + new BaseGenerationMiddleware() { + @Override + public String name() { + return "first"; + } + + @Override + public GenerationMiddleware newInstance() { + return this; + } + + @Override + public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next) + throws GenkitException { + order.add("first-before"); + ModelResponse resp = next.apply(ctx, params); + order.add("first-after"); + return resp; + } + }; + + GenerationMiddleware second = + new BaseGenerationMiddleware() { + @Override + public String name() { + return "second"; + } + + @Override + public GenerationMiddleware newInstance() { + return this; + } + + @Override + public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next) + throws GenkitException { + order.add("second-before"); + ModelResponse resp = next.apply(ctx, params); + order.add("second-after"); + return resp; + } + }; + + List middlewares = List.of(first, second); + ModelNext core = + (c, p) -> { + order.add("model"); + return responseWithText("result"); + }; + + ModelNext chain = core; + for (int i = middlewares.size() - 1; i >= 0; i--) { + GenerationMiddleware mw = middlewares.get(i); + ModelNext wrapped = chain; + chain = (c, p) -> mw.wrapModel(c, p, wrapped); + } + + chain.apply(ctx, new ModelParams(ModelRequest.builder().build(), null)); + + assertEquals( + List.of("first-before", "second-before", "model", "second-after", "first-after"), order); + } + + @Test + void testChainToolHooks_nestedOrder() { + List order = new ArrayList<>(); + + GenerationMiddleware first = + new BaseGenerationMiddleware() { + @Override + public String name() { + return "first"; + } + + @Override + public GenerationMiddleware newInstance() { + return this; + } + + @Override + public ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next) + throws GenkitException { + order.add("first-before"); + ToolResponse resp = next.apply(ctx, params); + order.add("first-after"); + return resp; + } + }; + + GenerationMiddleware second = + new BaseGenerationMiddleware() { + @Override + public String name() { + return "second"; + } + + @Override + public GenerationMiddleware newInstance() { + return this; + } + + @Override + public ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next) + throws GenkitException { + order.add("second-before"); + ToolResponse resp = next.apply(ctx, params); + order.add("second-after"); + return resp; + } + }; + + List middlewares = List.of(first, second); + ToolNext core = + (c, p) -> { + order.add("tool"); + return new ToolResponse(p.getRequest().getName(), "output"); + }; + + ToolNext chain = core; + for (int i = middlewares.size() - 1; i >= 0; i--) { + GenerationMiddleware mw = middlewares.get(i); + ToolNext wrapped = chain; + chain = (c, p) -> mw.wrapTool(c, p, wrapped); + } + + ToolRequest toolReq = new ToolRequest("myTool", Map.of()); + chain.apply(ctx, new ToolParams(toolReq, createTestTool("myTool"))); + + assertEquals( + List.of("first-before", "second-before", "tool", "second-after", "first-after"), order); + } + + // ========================================================================= + // newInstance() isolation + // ========================================================================= + + @Test + void testNewInstance_isolatesState() { + AtomicInteger sharedCounter = new AtomicInteger(0); + + GenerationMiddleware template = + new BaseGenerationMiddleware() { + private final AtomicInteger calls = new AtomicInteger(0); + + @Override + public String name() { + return "stateful"; + } + + @Override + public GenerationMiddleware newInstance() { + // Each instance gets its own counter + return new BaseGenerationMiddleware() { + private final AtomicInteger instanceCalls = new AtomicInteger(0); + + @Override + public String name() { + return "stateful"; + } + + @Override + public GenerationMiddleware newInstance() { + return this; + } + + @Override + public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next) + throws GenkitException { + instanceCalls.incrementAndGet(); + sharedCounter.incrementAndGet(); + return next.apply(ctx, params); + } + }; + } + }; + + // Simulate two generate() calls creating separate instances + GenerationMiddleware instance1 = template.newInstance(); + GenerationMiddleware instance2 = template.newInstance(); + + ModelResponse resp = responseWithText("ok"); + ModelNext passThrough = (c, p) -> resp; + ModelParams params = new ModelParams(ModelRequest.builder().build(), null); + + // Call instance1 three times + instance1.wrapModel(ctx, params, passThrough); + instance1.wrapModel(ctx, params, passThrough); + instance1.wrapModel(ctx, params, passThrough); + + // Call instance2 once + instance2.wrapModel(ctx, params, passThrough); + + // Shared counter sees all 4 calls + assertEquals(4, sharedCounter.get()); + + // But instances are independent (verified by the fact that both ran without error) + } + + // ========================================================================= + // Helper + // ========================================================================= + + private static Tool createTestTool(String name) { + Map schema = new HashMap<>(); + schema.put("type", "string"); + return new Tool<>(name, "Test tool", schema, schema, String.class, (ctx, input) -> "result"); + } +} diff --git a/core/src/main/java/com/google/genkit/core/middleware/Middleware.java b/core/src/main/java/com/google/genkit/core/middleware/Middleware.java index db508e7d8..4479eda02 100644 --- a/core/src/main/java/com/google/genkit/core/middleware/Middleware.java +++ b/core/src/main/java/com/google/genkit/core/middleware/Middleware.java @@ -41,7 +41,10 @@ * * @param The input type * @param The output type + * @deprecated Use {@code com.google.genkit.ai.middleware.GenerationMiddleware} instead, which + * supports distinct Generate, Model, and Tool hooks. */ +@Deprecated @FunctionalInterface public interface Middleware { diff --git a/genkit/src/main/java/com/google/genkit/Genkit.java b/genkit/src/main/java/com/google/genkit/Genkit.java index e6c1aed72..6f94acc39 100644 --- a/genkit/src/main/java/com/google/genkit/Genkit.java +++ b/genkit/src/main/java/com/google/genkit/Genkit.java @@ -20,6 +20,7 @@ import com.google.genkit.ai.*; import com.google.genkit.ai.evaluation.*; +import com.google.genkit.ai.middleware.*; import com.google.genkit.ai.session.*; import com.google.genkit.ai.telemetry.ModelTelemetryHelper; import com.google.genkit.core.*; @@ -658,85 +659,267 @@ private ModelResponse generateInternal(GenerateOptions options) throws Genkit ActionContext ctx = new ActionContext(registry); int maxTurns = options.getMaxTurns() != null ? options.getMaxTurns() : 5; - int turn = 0; + + // Create fresh middleware instances for this invocation + List middlewares = createMiddlewareInstances(options.getUse()); // Handle resume option if provided if (options.getResume() != null) { request = handleResumeOption(request, options); } - while (turn < maxTurns) { - // Create span metadata for the model call - SpanMetadata modelSpanMetadata = - SpanMetadata.builder() - .name(options.getModel()) - .type(ActionType.MODEL.getValue()) - .subtype("model") - .build(); + // Build model call wrapped with WrapModel hooks + ModelNext wrappedModelCall = buildWrappedModelCall(model, options, ctx, middlewares); - String flowName = ctx.getFlowName(); - if (flowName != null) { - modelSpanMetadata.getAttributes().put("genkit:metadata:flow:name", flowName); - } + // Use an array to hold the reference for recursive WrapGenerate wrapping + final GenerateNext[] generateRef = new GenerateNext[1]; - final ModelRequest currentRequest = request; - final String flowNameForTelemetry = flowName; - final String spanPath = "/generate/" + options.getModel(); - ModelResponse response = - Tracer.runInNewSpan( - ctx, + // Core generate iteration: model call → tool handling → recurse + GenerateNext rawGenerate = + (actx, params) -> { + ModelRequest req = params.getRequest(); + int turn = params.getIteration(); + + if (turn >= maxTurns) { + throw new GenkitException("Max tool execution turns (" + maxTurns + ") exceeded"); + } + + // Call model through WrapModel chain + ModelParams mparams = new ModelParams(req, null); + ModelResponse response = wrappedModelCall.apply(actx, mparams); + + // Check if the model requested tool calls + List toolRequestParts = extractToolRequestParts(response); + if (toolRequestParts.isEmpty()) { + return response; + } + + // Execute tools through WrapTool chain + ToolExecutionResult toolResult = + executeToolsWithMiddleware(actx, toolRequestParts, options.getTools(), middlewares); + + // If there are interrupts, return immediately + if (!toolResult.getInterrupts().isEmpty()) { + return buildInterruptedResponse(response, toolResult); + } + + // Build next request with updated messages + Message assistantMessage = response.getMessage(); + List updatedMessages = new java.util.ArrayList<>(req.getMessages()); + updatedMessages.add(assistantMessage); + + Message toolResponseMessage = new Message(); + toolResponseMessage.setRole(Role.TOOL); + toolResponseMessage.setContent(toolResult.getResponses()); + updatedMessages.add(toolResponseMessage); + + ModelRequest nextRequest = + ModelRequest.builder() + .messages(updatedMessages) + .config(req.getConfig()) + .tools(req.getTools()) + .output(req.getOutput()) + .build(); + + // Recurse through the wrapped generate function (goes through WrapGenerate hooks) + return generateRef[0].apply(actx, new GenerateParams(nextRequest, turn + 1)); + }; + + // Chain WrapGenerate hooks around the core iteration + generateRef[0] = chainGenerateHooks(middlewares, rawGenerate); + + // Start generation + return generateRef[0].apply(ctx, new GenerateParams(request, 0)); + } + + /** Creates fresh middleware instances for a single generate invocation. */ + private List createMiddlewareInstances(List use) { + if (use == null || use.isEmpty()) { + return List.of(); + } + return use.stream().map(GenerationMiddleware::newInstance).toList(); + } + + /** Builds the model call function wrapped with WrapModel hooks from middleware. */ + private ModelNext buildWrappedModelCall( + Model model, + GenerateOptions options, + ActionContext ctx, + List middlewares) { + + // Core model call with telemetry + ModelNext core = + (actx, mparams) -> { + ModelRequest req = mparams.getRequest(); + + SpanMetadata modelSpanMetadata = + SpanMetadata.builder() + .name(options.getModel()) + .type(ActionType.MODEL.getValue()) + .subtype("model") + .build(); + + String flowName = actx.getFlowName(); + if (flowName != null) { + modelSpanMetadata.getAttributes().put("genkit:metadata:flow:name", flowName); + } + + final String spanPath = "/generate/" + options.getModel(); + return Tracer.runInNewSpan( + actx, modelSpanMetadata, - request, - (spanCtx, req) -> { - // Wrap model execution with telemetry to record generate metrics + req, + (spanCtx, r) -> { return ModelTelemetryHelper.runWithTelemetry( options.getModel(), - flowNameForTelemetry, + flowName, spanPath, - currentRequest, - r -> model.run(ctx.withSpanContext(spanCtx), r)); + req, + mr -> model.run(actx.withSpanContext(spanCtx), mr)); }); + }; - // Check if the model requested tool calls - List toolRequestParts = extractToolRequestParts(response); - if (toolRequestParts.isEmpty()) { - // No tool calls, return the response - return response; - } + return chainModelHooks(middlewares, core); + } + + /** Chains WrapGenerate hooks. First middleware is outermost. */ + private GenerateNext chainGenerateHooks( + List middlewares, GenerateNext core) { + if (middlewares.isEmpty()) { + return core; + } + GenerateNext current = core; + for (int i = middlewares.size() - 1; i >= 0; i--) { + final GenerationMiddleware mw = middlewares.get(i); + final GenerateNext next = current; + current = (ctx, params) -> mw.wrapGenerate(ctx, params, next); + } + return current; + } + + /** Chains WrapModel hooks. First middleware is outermost. */ + private ModelNext chainModelHooks(List middlewares, ModelNext core) { + if (middlewares.isEmpty()) { + return core; + } + ModelNext current = core; + for (int i = middlewares.size() - 1; i >= 0; i--) { + final GenerationMiddleware mw = middlewares.get(i); + final ModelNext next = current; + current = (ctx, params) -> mw.wrapModel(ctx, params, next); + } + return current; + } + + /** Chains WrapTool hooks. First middleware is outermost. */ + private ToolNext chainToolHooks(List middlewares, ToolNext core) { + if (middlewares.isEmpty()) { + return core; + } + ToolNext current = core; + for (int i = middlewares.size() - 1; i >= 0; i--) { + final GenerationMiddleware mw = middlewares.get(i); + final ToolNext next = current; + current = (ctx, params) -> mw.wrapTool(ctx, params, next); + } + return current; + } + + /** Executes tools with WrapTool middleware hooks applied. */ + private ToolExecutionResult executeToolsWithMiddleware( + ActionContext ctx, + List toolRequestParts, + List> tools, + List middlewares) { + + // Build WrapTool chain + ToolNext wrappedToolCall = + chainToolHooks( + middlewares, + (actx, tparams) -> { + Tool tool = tparams.getTool(); + ToolRequest toolReq = tparams.getRequest(); + + Object toolInput = toolReq.getInput(); + Class inputClass = tool.getInputClass(); + if (inputClass != null && toolInput != null && !inputClass.isInstance(toolInput)) { + toolInput = JsonUtils.convert(toolInput, inputClass); + } + + @SuppressWarnings("unchecked") + Tool typedTool = (Tool) tool; + Object result = typedTool.run(actx, toolInput); + + return new ToolResponse(toolReq.getRef(), toolReq.getName(), result); + }); + + List responseParts = new java.util.ArrayList<>(); + List interrupts = new java.util.ArrayList<>(); + Map interruptMap = new java.util.HashMap<>(); + Map pendingOutputMap = new java.util.HashMap<>(); - // Execute tools and handle interrupts - ToolExecutionResult toolResult = - executeToolsWithInterruptHandling(ctx, toolRequestParts, options.getTools()); + for (Part toolRequestPart : toolRequestParts) { + ToolRequest toolRequest = toolRequestPart.getToolRequest(); + String toolName = toolRequest.getName(); + String key = toolName + "#" + (toolRequest.getRef() != null ? toolRequest.getRef() : ""); - // If there are interrupts, return immediately with interrupted response - if (!toolResult.getInterrupts().isEmpty()) { - return buildInterruptedResponse(response, toolResult); + Tool tool = findTool(toolName, tools); + if (tool == null) { + Part errorPart = new Part(); + ToolResponse errorResponse = + new ToolResponse( + toolRequest.getRef(), toolName, Map.of("error", "Tool not found: " + toolName)); + errorPart.setToolResponse(errorResponse); + responseParts.add(errorPart); + continue; } - // Add the assistant message with tool requests - Message assistantMessage = response.getMessage(); - List updatedMessages = new java.util.ArrayList<>(request.getMessages()); - updatedMessages.add(assistantMessage); + try { + // Execute through WrapTool chain + ToolParams tparams = new ToolParams(toolRequest, tool); + ToolResponse toolResponse = wrappedToolCall.apply(ctx, tparams); - // Add tool response message - Message toolResponseMessage = new Message(); - toolResponseMessage.setRole(Role.TOOL); - toolResponseMessage.setContent(toolResult.getResponses()); - updatedMessages.add(toolResponseMessage); + Part responsePart = new Part(); + responsePart.setToolResponse(toolResponse); + responseParts.add(responsePart); - // Update request with new messages for next turn - request = - ModelRequest.builder() - .messages(updatedMessages) - .config(request.getConfig()) - .tools(request.getTools()) - .output(request.getOutput()) - .build(); + pendingOutputMap.put(key, toolResponse.getOutput()); - turn++; + logger.debug("Executed tool '{}' successfully", toolName); + + } catch (ToolInterruptException e) { + Map interruptMetadata = e.getMetadata(); + + Part interruptPart = new Part(); + interruptPart.setToolRequest(toolRequest); + Map metadata = + toolRequestPart.getMetadata() != null + ? new java.util.HashMap<>(toolRequestPart.getMetadata()) + : new java.util.HashMap<>(); + metadata.put( + "interrupt", + interruptMetadata != null && !interruptMetadata.isEmpty() ? interruptMetadata : true); + interruptPart.setMetadata(metadata); + + interrupts.add(interruptPart); + interruptMap.put(key, interruptPart); + + logger.debug("Tool '{}' triggered interrupt", toolName); + + } catch (Exception e) { + logger.error("Tool execution failed for '{}': {}", toolName, e.getMessage()); + Part errorPart = new Part(); + ToolResponse errorResponse = + new ToolResponse( + toolRequest.getRef(), + toolName, + Map.of("error", "Tool execution failed: " + e.getMessage())); + errorPart.setToolResponse(errorResponse); + responseParts.add(errorPart); + } } - throw new GenkitException("Max tool execution turns (" + maxTurns + ") exceeded"); + return new ToolExecutionResult(responseParts, interrupts, interruptMap, pendingOutputMap); } /** Handles resume options by processing respond and restart directives. */ diff --git a/pom.xml b/pom.xml index ffb0bc5f6..42210896c 100644 --- a/pom.xml +++ b/pom.xml @@ -104,6 +104,7 @@ samples/evaluators-plugin samples/complex-io samples/middleware + samples/middleware-v2 samples/mcp samples/chat-session samples/multi-agent diff --git a/samples/middleware-v2/README.md b/samples/middleware-v2/README.md new file mode 100644 index 000000000..5726e101c --- /dev/null +++ b/samples/middleware-v2/README.md @@ -0,0 +1,161 @@ +# Genkit Java Middleware V2 Sample + +This sample demonstrates the **V2 GenerationMiddleware** system, which provides three distinct hooks into the generation pipeline: + +- **WrapGenerate** — wraps each iteration of the tool loop +- **WrapModel** — wraps each model API call +- **WrapTool** — wraps each tool execution + +Unlike V1 middleware (which wraps flows), V2 middleware is attached per `generate()` call via `GenerateOptions.builder().use()` and hooks directly into the AI generation pipeline. + +## Prerequisites + +- Java 21+ +- Maven 3.6+ +- OpenAI API key + +## Running the Sample + +### Option 1: Direct Run + +```bash +# Set your OpenAI API key +export OPENAI_API_KEY=your-api-key-here + +# Navigate to the sample directory +cd java/samples/middleware-v2 + +# Run the sample +./run.sh +# Or: mvn compile exec:java +``` + +### Option 2: With Genkit Dev UI (Recommended) + +```bash +# Set your OpenAI API key +export OPENAI_API_KEY=your-api-key-here + +# Navigate to the sample directory +cd java/samples/middleware-v2 + +# Run with Genkit CLI +genkit start -- ./run.sh +``` + +The Dev UI will be available at http://localhost:4000 + +## Middleware Examples + +### 1. ModelLoggingMiddleware (WrapModel) +Logs every model API call with a per-invocation counter. Demonstrates `newInstance()` for fresh state per `generate()` call. + +### 2. GenerateTimingMiddleware (WrapGenerate) +Measures wall-clock time for each generate loop iteration (model call + tool execution). + +### 3. ToolMonitorMiddleware (WrapTool) +Logs tool execution name and duration. Stateless — `newInstance()` returns `this`. + +### 4. FullObservabilityMiddleware (All 3 hooks) +A single middleware that implements all three hooks, showing how one middleware can observe the entire pipeline with per-invocation counters. + +## Available Endpoints + +| Endpoint | Description | Middleware | +|----------|-------------|------------| +| `/v2-chat` | AI chat | Model logging + generate timing | +| `/v2-observable` | AI chat | Full observability (all 3 hooks) | +| `/v2-stacked` | AI chat | Three separate middleware stacked | +| `/v2-baseline` | AI chat | No middleware (baseline) | + +## Example Requests + +```bash +# Chat with model logging + timing +curl -X POST http://localhost:8080/v2-chat \ + -H 'Content-Type: application/json' \ + -d '"What is middleware?"' + +# Chat with full observability +curl -X POST http://localhost:8080/v2-observable \ + -H 'Content-Type: application/json' \ + -d '"Explain Java records"' + +# Chat with stacked middleware +curl -X POST http://localhost:8080/v2-stacked \ + -H 'Content-Type: application/json' \ + -d '"Hello world"' + +# Baseline (no middleware) +curl -X POST http://localhost:8080/v2-baseline \ + -H 'Content-Type: application/json' \ + -d '"Hello world"' +``` + +## Creating Custom V2 Middleware + +Extend `BaseGenerationMiddleware` and override only the hooks you need: + +```java +import com.google.genkit.ai.middleware.*; +import com.google.genkit.core.ActionContext; + +public class MyMiddleware extends BaseGenerationMiddleware { + + @Override + public String name() { return "my-middleware"; } + + @Override + public GenerationMiddleware newInstance() { return new MyMiddleware(); } + + @Override + public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next) + throws GenkitException { + System.out.println("Before model call"); + ModelResponse resp = next.apply(ctx, params); + System.out.println("After model call: " + resp.getText().length() + " chars"); + return resp; + } +} +``` + +Then attach it to a `generate()` call: + +```java +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("Hello") + .use(new MyMiddleware()) + .build()); +``` + +## Architecture + +V2 middleware wraps the generation pipeline at three levels: + +``` +generate() call + └─ WrapGenerate (per tool-loop iteration) + └─ WrapModel (per model API call) + └─ WrapTool (per tool execution) + └─ recurse → next WrapGenerate iteration +``` + +Each `generate()` call creates fresh middleware instances via `newInstance()`, enabling per-invocation state (counters, timers) without shared mutable state across requests. + +Middleware are chained in order — the first middleware in the `use()` list is the outermost wrapper. + +## V1 vs V2 Middleware + +| | V1 (`Middleware`) | V2 (`GenerationMiddleware`) | +|---|---|---| +| **Scope** | Wraps flows | Wraps generation pipeline | +| **Hooks** | Single `apply()` | 3 hooks: Generate, Model, Tool | +| **Attachment** | `defineFlow(..., middleware)` | `GenerateOptions.builder().use(...)` | +| **State** | Shared across calls | Fresh per `generate()` via `newInstance()` | + +## See Also + +- [V1 Middleware Sample](../middleware/) — flow-level middleware +- [Genkit Documentation](https://github.com/genkit-ai/genkit-java) diff --git a/samples/middleware-v2/pom.xml b/samples/middleware-v2/pom.xml new file mode 100644 index 000000000..0857d4462 --- /dev/null +++ b/samples/middleware-v2/pom.xml @@ -0,0 +1,91 @@ + + + + 4.0.0 + + + com.google.genkit + genkit-parent + 1.0.0-SNAPSHOT + ../../pom.xml + + + com.google.genkit.samples + genkit-sample-middleware-v2 + jar + Genkit Middleware V2 Sample + Sample application demonstrating V2 GenerationMiddleware with Generate, Model, and Tool hooks + + + UTF-8 + 21 + 21 + 1.0.0-SNAPSHOT + true + true + + + + + com.google.genkit + genkit + ${genkit.version} + + + com.google.genkit + genkit-plugin-openai + ${genkit.version} + + + com.google.genkit + genkit-plugin-jetty + ${genkit.version} + + + ch.qos.logback + logback-classic + 1.5.32 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.15.0 + + 21 + 21 + + + + org.codehaus.mojo + exec-maven-plugin + 3.6.3 + + com.google.genkit.samples.MiddlewareV2Sample + + + + + diff --git a/samples/middleware-v2/run.sh b/samples/middleware-v2/run.sh new file mode 100755 index 000000000..54805f669 --- /dev/null +++ b/samples/middleware-v2/run.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +# Run the Genkit Middleware V2 Sample + +set -e + +# Navigate to the sample directory +cd "$(dirname "$0")" + +# Check for OPENAI_API_KEY +if [ -z "$OPENAI_API_KEY" ]; then + echo "Warning: OPENAI_API_KEY is not set. The sample may not work correctly." + echo "Set it with: export OPENAI_API_KEY=your-api-key" +fi + +# Build and run +echo "Building and running Genkit Middleware V2 Sample..." +mvn compile exec:java -q diff --git a/samples/middleware-v2/src/main/java/com/google/genkit/samples/MiddlewareV2Sample.java b/samples/middleware-v2/src/main/java/com/google/genkit/samples/MiddlewareV2Sample.java new file mode 100644 index 000000000..7c6f288b5 --- /dev/null +++ b/samples/middleware-v2/src/main/java/com/google/genkit/samples/MiddlewareV2Sample.java @@ -0,0 +1,402 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.genkit.samples; + +import com.google.genkit.Genkit; +import com.google.genkit.GenkitOptions; +import com.google.genkit.ai.GenerateOptions; +import com.google.genkit.ai.GenerationConfig; +import com.google.genkit.ai.ModelResponse; +import com.google.genkit.ai.Tool; +import com.google.genkit.ai.ToolResponse; +import com.google.genkit.ai.middleware.BaseGenerationMiddleware; +import com.google.genkit.ai.middleware.GenerateNext; +import com.google.genkit.ai.middleware.GenerateParams; +import com.google.genkit.ai.middleware.GenerationMiddleware; +import com.google.genkit.ai.middleware.ModelNext; +import com.google.genkit.ai.middleware.ModelParams; +import com.google.genkit.ai.middleware.ToolNext; +import com.google.genkit.ai.middleware.ToolParams; +import com.google.genkit.core.ActionContext; +import com.google.genkit.core.Flow; +import com.google.genkit.core.GenkitException; +import com.google.genkit.plugins.jetty.JettyPlugin; +import com.google.genkit.plugins.jetty.JettyPluginOptions; +import com.google.genkit.plugins.openai.OpenAIPlugin; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Sample application demonstrating the V2 GenerationMiddleware system. + * + *

V2 middleware hooks into three distinct stages of the generation pipeline: + * + *

    + *
  • WrapGenerate — wraps each iteration of the tool loop + *
  • WrapModel — wraps each model API call + *
  • WrapTool — wraps each tool execution + *
+ * + *

Middleware is attached per {@code generate()} call via {@code GenerateOptions.builder().use()} + * rather than per flow. + * + *

Each {@code generate()} call creates a fresh middleware instance via {@code newInstance()}, + * enabling per-invocation state (counters, timers) without shared mutable state across requests. + * + *

To run: + * + *

    + *
  1. Set the OPENAI_API_KEY environment variable + *
  2. Run: mvn exec:java + *
+ */ +public class MiddlewareV2Sample { + + private static final Logger logger = LoggerFactory.getLogger(MiddlewareV2Sample.class); + + // ========================================================================= + // Example 1: Model logging middleware (WrapModel hook) + // ========================================================================= + + /** + * Logs every model API call with a call counter. The counter resets per generate() invocation + * because {@code newInstance()} returns a fresh object. + */ + static class ModelLoggingMiddleware extends BaseGenerationMiddleware { + + private final AtomicInteger modelCalls = new AtomicInteger(0); + + @Override + public String name() { + return "model-logging"; + } + + @Override + public GenerationMiddleware newInstance() { + return new ModelLoggingMiddleware(); + } + + @Override + public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next) + throws GenkitException { + int callNum = modelCalls.incrementAndGet(); + logger.info("[model-logging] Model call #{}", callNum); + ModelResponse resp = next.apply(ctx, params); + logger.info( + "[model-logging] Model call #{} returned ({} chars)", + callNum, + resp.getText() != null ? resp.getText().length() : 0); + return resp; + } + } + + // ========================================================================= + // Example 2: Generate timing middleware (WrapGenerate hook) + // ========================================================================= + + /** + * Measures the wall-clock time of each generate loop iteration including model call + tool + * execution within that iteration. + */ + static class GenerateTimingMiddleware extends BaseGenerationMiddleware { + + @Override + public String name() { + return "generate-timing"; + } + + @Override + public GenerationMiddleware newInstance() { + return new GenerateTimingMiddleware(); + } + + @Override + public ModelResponse wrapGenerate(ActionContext ctx, GenerateParams params, GenerateNext next) + throws GenkitException { + long start = System.currentTimeMillis(); + logger.info("[generate-timing] Starting iteration {}", params.getIteration()); + ModelResponse resp = next.apply(ctx, params); + logger.info( + "[generate-timing] Iteration {} completed in {}ms", + params.getIteration(), + System.currentTimeMillis() - start); + return resp; + } + } + + // ========================================================================= + // Example 3: Tool monitor middleware (WrapTool hook) + // ========================================================================= + + /** Logs tool execution name and duration. Stateless, so newInstance() returns {@code this}. */ + static class ToolMonitorMiddleware extends BaseGenerationMiddleware { + + @Override + public String name() { + return "tool-monitor"; + } + + @Override + public GenerationMiddleware newInstance() { + return this; // stateless — safe to reuse + } + + @Override + public ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next) + throws GenkitException { + String toolName = params.getRequest().getName(); + logger.info("[tool-monitor] Executing tool: {}", toolName); + long start = System.currentTimeMillis(); + ToolResponse resp = next.apply(ctx, params); + logger.info( + "[tool-monitor] Tool {} completed in {}ms", toolName, System.currentTimeMillis() - start); + return resp; + } + } + + // ========================================================================= + // Example 4: Combined multi-hook middleware + // ========================================================================= + + /** + * A single middleware that implements all three hooks. Demonstrates that one middleware can + * observe every stage of the pipeline. + */ + static class FullObservabilityMiddleware extends BaseGenerationMiddleware { + + private final AtomicInteger iterations = new AtomicInteger(0); + private final AtomicInteger modelCalls = new AtomicInteger(0); + private final AtomicInteger toolCalls = new AtomicInteger(0); + + @Override + public String name() { + return "full-observability"; + } + + @Override + public GenerationMiddleware newInstance() { + return new FullObservabilityMiddleware(); + } + + @Override + public ModelResponse wrapGenerate(ActionContext ctx, GenerateParams params, GenerateNext next) + throws GenkitException { + int iter = iterations.incrementAndGet(); + logger.info("[observability] === Generate iteration {} ===", iter); + ModelResponse resp = next.apply(ctx, params); + logger.info( + "[observability] === Iteration {} done (model calls: {}, tool calls: {}) ===", + iter, + modelCalls.get(), + toolCalls.get()); + return resp; + } + + @Override + public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next) + throws GenkitException { + int call = modelCalls.incrementAndGet(); + logger.info("[observability] Model call #{}", call); + return next.apply(ctx, params); + } + + @Override + public ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next) + throws GenkitException { + int call = toolCalls.incrementAndGet(); + logger.info("[observability] Tool call #{}: {}", call, params.getRequest().getName()); + return next.apply(ctx, params); + } + } + + // ========================================================================= + // Main + // ========================================================================= + + public static void main(String[] args) throws Exception { + JettyPlugin jetty = new JettyPlugin(JettyPluginOptions.builder().port(8080).build()); + + Genkit genkit = + Genkit.builder() + .options(GenkitOptions.builder().devMode(true).reflectionPort(3100).build()) + .plugin(OpenAIPlugin.create()) + .plugin(jetty) + .build(); + + // Instantiate middleware (templates — newInstance() is called per generate()) + GenerationMiddleware modelLogging = new ModelLoggingMiddleware(); + GenerationMiddleware generateTiming = new GenerateTimingMiddleware(); + GenerationMiddleware toolMonitor = new ToolMonitorMiddleware(); + GenerationMiddleware fullObservability = new FullObservabilityMiddleware(); + + // Define a simple tool so the WrapTool hook gets exercised + @SuppressWarnings("unchecked") + Tool, Map> weatherTool = + genkit.defineTool( + "getWeather", + "Gets the current weather for a given city", + Map.of( + "type", + "object", + "properties", + Map.of("city", Map.of("type", "string", "description", "The city name")), + "required", + new String[] {"city"}), + (Class>) (Class) Map.class, + (ctx, input) -> { + String city = (String) input.get("city"); + Map weather = new HashMap<>(); + weather.put("city", city); + weather.put("temperature", "22°C"); + weather.put("conditions", "Sunny"); + return weather; + }); + + // ======================================================= + // Flow 1: Simple chat with model logging + generate timing + // ======================================================= + + Flow chatFlow = + genkit.defineFlow( + "v2-chat", + String.class, + String.class, + (ctx, userMessage) -> { + ModelResponse response = + genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .system("You are a helpful assistant. Be concise.") + .prompt(userMessage) + .use(modelLogging, generateTiming) + .config( + GenerationConfig.builder() + .temperature(0.7) + .maxOutputTokens(200) + .build()) + .build()); + return response.getText(); + }); + + // ======================================================= + // Flow 2: Chat with all three hooks via full observability + // ======================================================= + + Flow observableFlow = + genkit.defineFlow( + "v2-observable", + String.class, + String.class, + (ctx, userMessage) -> { + ModelResponse response = + genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .system( + "You are a helpful assistant. Use the getWeather tool when asked about weather.") + .prompt(userMessage) + .tools(List.of(weatherTool)) + .use(fullObservability) + .config( + GenerationConfig.builder() + .temperature(0.7) + .maxOutputTokens(300) + .build()) + .build()); + return response.getText(); + }); + + // ======================================================= + // Flow 3: Stacking multiple middleware together + // ======================================================= + + Flow stackedFlow = + genkit.defineFlow( + "v2-stacked", + String.class, + String.class, + (ctx, userMessage) -> { + ModelResponse response = + genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt(userMessage) + .use(modelLogging, generateTiming, toolMonitor) + .config( + GenerationConfig.builder() + .temperature(0.7) + .maxOutputTokens(200) + .build()) + .build()); + return response.getText(); + }); + + // ======================================================= + // Flow 4: No middleware (baseline for comparison) + // ======================================================= + + Flow baselineFlow = + genkit.defineFlow( + "v2-baseline", + String.class, + String.class, + (ctx, userMessage) -> { + ModelResponse response = + genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt(userMessage) + .config( + GenerationConfig.builder() + .temperature(0.7) + .maxOutputTokens(200) + .build()) + .build()); + return response.getText(); + }); + + logger.info("\n========================================"); + logger.info("Genkit Middleware V2 Sample Started!"); + logger.info("========================================\n"); + + logger.info("Available flows:"); + logger.info(" - v2-chat: Model logging + generate timing middleware"); + logger.info(" - v2-observable: Full observability (all 3 hooks in one middleware)"); + logger.info(" - v2-stacked: Three separate middleware stacked together"); + logger.info(" - v2-baseline: No middleware (baseline comparison)\n"); + + logger.info("Server running on http://localhost:8080"); + logger.info("Reflection server running on http://localhost:3100"); + logger.info("\nExample requests:"); + logger.info( + " curl -X POST http://localhost:8080/v2-chat -H 'Content-Type: application/json' -d '\"What is middleware?\"'"); + logger.info( + " curl -X POST http://localhost:8080/v2-observable -H 'Content-Type: application/json' -d '\"Explain Java records\"'"); + logger.info( + " curl -X POST http://localhost:8080/v2-stacked -H 'Content-Type: application/json' -d '\"Hello world\"'"); + logger.info( + " curl -X POST http://localhost:8080/v2-baseline -H 'Content-Type: application/json' -d '\"Hello world\"'"); + + jetty.start(); + } +} diff --git a/samples/middleware-v2/src/main/resources/logback.xml b/samples/middleware-v2/src/main/resources/logback.xml new file mode 100644 index 000000000..fe98c37a8 --- /dev/null +++ b/samples/middleware-v2/src/main/resources/logback.xml @@ -0,0 +1,26 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + + + From af6c77b323d18e3120d589201cfae4ba90bbf183 Mon Sep 17 00:00:00 2001 From: xavidop Date: Mon, 30 Mar 2026 01:28:53 +0200 Subject: [PATCH 02/50] fix: code assist feedback --- .../main/java/com/google/genkit/Genkit.java | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/genkit/src/main/java/com/google/genkit/Genkit.java b/genkit/src/main/java/com/google/genkit/Genkit.java index 6f94acc39..e4dc9ac97 100644 --- a/genkit/src/main/java/com/google/genkit/Genkit.java +++ b/genkit/src/main/java/com/google/genkit/Genkit.java @@ -663,6 +663,35 @@ private ModelResponse generateInternal(GenerateOptions options) throws Genkit // Create fresh middleware instances for this invocation List middlewares = createMiddlewareInstances(options.getUse()); + // Collect tools from middleware instances and merge with options tools + List> allTools = new ArrayList<>(); + if (options.getTools() != null) { + allTools.addAll(options.getTools()); + } + for (GenerationMiddleware mw : middlewares) { + List> mwTools = mw.tools(); + if (mwTools != null && !mwTools.isEmpty()) { + allTools.addAll(mwTools); + } + } + + // Add middleware tool definitions to the model request + if (allTools.size() > (options.getTools() != null ? options.getTools().size() : 0)) { + List allToolDefs = new ArrayList<>(); + if (request.getTools() != null) { + allToolDefs.addAll(request.getTools()); + } + for (GenerationMiddleware mw : middlewares) { + List> mwTools = mw.tools(); + if (mwTools != null) { + for (Tool t : mwTools) { + allToolDefs.add(t.getDefinition()); + } + } + } + request.setTools(allToolDefs); + } + // Handle resume option if provided if (options.getResume() != null) { request = handleResumeOption(request, options); @@ -694,9 +723,9 @@ private ModelResponse generateInternal(GenerateOptions options) throws Genkit return response; } - // Execute tools through WrapTool chain + // Execute tools through WrapTool chain (includes middleware-provided tools) ToolExecutionResult toolResult = - executeToolsWithMiddleware(actx, toolRequestParts, options.getTools(), middlewares); + executeToolsWithMiddleware(actx, toolRequestParts, allTools, middlewares); // If there are interrupts, return immediately if (!toolResult.getInterrupts().isEmpty()) { From ef9de78ae4537d43d85971e09fcb3cfb5b6bc5de Mon Sep 17 00:00:00 2001 From: xavidop Date: Mon, 13 Apr 2026 16:34:21 +0200 Subject: [PATCH 03/50] fix: use Part instead of ToolResponse --- .gitignore | 1 + .../middleware/BaseGenerationMiddleware.java | 5 +-- .../ai/middleware/GenerationMiddleware.java | 8 ++-- .../google/genkit/ai/middleware/ToolNext.java | 6 +-- .../genkit/ai/middleware/ToolParams.java | 18 +++++--- .../middleware/GenerationMiddlewareTest.java | 41 ++++++++++--------- .../main/java/com/google/genkit/Genkit.java | 11 +++-- .../genkit/samples/MiddlewareV2Sample.java | 8 ++-- 8 files changed, 53 insertions(+), 45 deletions(-) diff --git a/.gitignore b/.gitignore index 4201b96e8..37a71bf45 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ package-lock.json *.log hs_err_pid* samples/google-genai/generated_media/ +.astro \ No newline at end of file diff --git a/ai/src/main/java/com/google/genkit/ai/middleware/BaseGenerationMiddleware.java b/ai/src/main/java/com/google/genkit/ai/middleware/BaseGenerationMiddleware.java index d327ea579..0009468c8 100644 --- a/ai/src/main/java/com/google/genkit/ai/middleware/BaseGenerationMiddleware.java +++ b/ai/src/main/java/com/google/genkit/ai/middleware/BaseGenerationMiddleware.java @@ -19,8 +19,8 @@ package com.google.genkit.ai.middleware; import com.google.genkit.ai.ModelResponse; +import com.google.genkit.ai.Part; import com.google.genkit.ai.Tool; -import com.google.genkit.ai.ToolResponse; import com.google.genkit.core.ActionContext; import com.google.genkit.core.GenkitException; import java.util.Collections; @@ -66,8 +66,7 @@ public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext } @Override - public ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next) - throws GenkitException { + public Part wrapTool(ActionContext ctx, ToolParams params, ToolNext next) throws GenkitException { return next.apply(ctx, params); } diff --git a/ai/src/main/java/com/google/genkit/ai/middleware/GenerationMiddleware.java b/ai/src/main/java/com/google/genkit/ai/middleware/GenerationMiddleware.java index 27db05f05..bb751be28 100644 --- a/ai/src/main/java/com/google/genkit/ai/middleware/GenerationMiddleware.java +++ b/ai/src/main/java/com/google/genkit/ai/middleware/GenerationMiddleware.java @@ -19,8 +19,8 @@ package com.google.genkit.ai.middleware; import com.google.genkit.ai.ModelResponse; +import com.google.genkit.ai.Part; import com.google.genkit.ai.Tool; -import com.google.genkit.ai.ToolResponse; import com.google.genkit.core.ActionContext; import com.google.genkit.core.GenkitException; import java.util.Collections; @@ -107,12 +107,12 @@ ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next) * Implementations must be safe for concurrent use. * * @param ctx the action context - * @param params the tool parameters including the request and resolved tool + * @param params the tool parameters including the request part and resolved tool * @param next the next function in the chain - * @return the tool response + * @return the tool response part (includes part-level metadata) * @throws GenkitException if processing fails */ - ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next) throws GenkitException; + Part wrapTool(ActionContext ctx, ToolParams params, ToolNext next) throws GenkitException; /** * Returns additional tools to make available during generation. These tools are dynamically added diff --git a/ai/src/main/java/com/google/genkit/ai/middleware/ToolNext.java b/ai/src/main/java/com/google/genkit/ai/middleware/ToolNext.java index 53d79e835..fddbe6b71 100644 --- a/ai/src/main/java/com/google/genkit/ai/middleware/ToolNext.java +++ b/ai/src/main/java/com/google/genkit/ai/middleware/ToolNext.java @@ -18,7 +18,7 @@ package com.google.genkit.ai.middleware; -import com.google.genkit.ai.ToolResponse; +import com.google.genkit.ai.Part; import com.google.genkit.core.ActionContext; import com.google.genkit.core.GenkitException; @@ -31,8 +31,8 @@ public interface ToolNext { * * @param ctx the action context * @param params the tool parameters - * @return the tool response + * @return the tool response part (includes part-level metadata) * @throws GenkitException if processing fails */ - ToolResponse apply(ActionContext ctx, ToolParams params) throws GenkitException; + Part apply(ActionContext ctx, ToolParams params) throws GenkitException; } diff --git a/ai/src/main/java/com/google/genkit/ai/middleware/ToolParams.java b/ai/src/main/java/com/google/genkit/ai/middleware/ToolParams.java index 36ba87732..5b2e4b92d 100644 --- a/ai/src/main/java/com/google/genkit/ai/middleware/ToolParams.java +++ b/ai/src/main/java/com/google/genkit/ai/middleware/ToolParams.java @@ -18,29 +18,35 @@ package com.google.genkit.ai.middleware; +import com.google.genkit.ai.Part; import com.google.genkit.ai.Tool; import com.google.genkit.ai.ToolRequest; /** Holds parameters for the {@link GenerationMiddleware#wrapTool} hook. */ public class ToolParams { - private final ToolRequest request; + private final Part requestPart; private final Tool tool; /** * Creates ToolParams. * - * @param request the tool request about to be executed + * @param requestPart the tool request part (includes metadata) about to be executed * @param tool the resolved tool being called */ - public ToolParams(ToolRequest request, Tool tool) { - this.request = request; + public ToolParams(Part requestPart, Tool tool) { + this.requestPart = requestPart; this.tool = tool; } - /** Returns the tool request about to be executed. */ + /** Returns the full tool request part, including part-level metadata. */ + public Part getRequestPart() { + return requestPart; + } + + /** Convenience method: returns the tool request from the request part. */ public ToolRequest getRequest() { - return request; + return requestPart.getToolRequest(); } /** Returns the resolved tool being called. */ diff --git a/ai/src/test/java/com/google/genkit/ai/middleware/GenerationMiddlewareTest.java b/ai/src/test/java/com/google/genkit/ai/middleware/GenerationMiddlewareTest.java index 0b40f28c8..fd4184fe7 100644 --- a/ai/src/test/java/com/google/genkit/ai/middleware/GenerationMiddlewareTest.java +++ b/ai/src/test/java/com/google/genkit/ai/middleware/GenerationMiddlewareTest.java @@ -24,6 +24,7 @@ import com.google.genkit.ai.Message; import com.google.genkit.ai.ModelRequest; import com.google.genkit.ai.ModelResponse; +import com.google.genkit.ai.Part; import com.google.genkit.ai.Tool; import com.google.genkit.ai.ToolRequest; import com.google.genkit.ai.ToolResponse; @@ -231,12 +232,12 @@ void testModelNext_exceptionPropagates() { void testToolNext_passThrough() { ToolRequest toolReq = new ToolRequest("myTool", Map.of("key", "value")); Tool tool = createTestTool("myTool"); - ToolParams params = new ToolParams(toolReq, tool); - ToolResponse expected = new ToolResponse("myTool", "tool output"); + ToolParams params = new ToolParams(Part.toolRequest(toolReq), tool); + Part expected = Part.toolResponse(new ToolResponse("myTool", "tool output")); ToolNext next = (c, p) -> expected; - ToolResponse result = next.apply(ctx, params); + Part result = next.apply(ctx, params); assertSame(expected, result); } @@ -247,19 +248,19 @@ void testToolNext_chainOrder() { ToolNext core = (c, p) -> { order.add("tool-exec"); - return new ToolResponse(p.getRequest().getName(), "result"); + return Part.toolResponse(new ToolResponse(p.getRequest().getName(), "result")); }; ToolNext wrapper = (c, p) -> { order.add("before-tool"); - ToolResponse resp = core.apply(c, p); + Part resp = core.apply(c, p); order.add("after-tool"); return resp; }; ToolRequest toolReq = new ToolRequest("test", Map.of()); - wrapper.apply(ctx, new ToolParams(toolReq, createTestTool("test"))); + wrapper.apply(ctx, new ToolParams(Part.toolRequest(toolReq), createTestTool("test"))); assertEquals(List.of("before-tool", "tool-exec", "after-tool"), order); } @@ -273,11 +274,11 @@ void testToolNext_accessesToolInfo() { (c, p) -> { assertEquals("weatherTool", p.getRequest().getName()); assertEquals("weatherTool", p.getTool().getName()); - return new ToolResponse("weatherTool", "sunny"); + return Part.toolResponse(new ToolResponse("weatherTool", "sunny")); }; - ToolResponse resp = next.apply(ctx, new ToolParams(toolReq, tool)); - assertEquals("weatherTool", resp.getName()); + Part resp = next.apply(ctx, new ToolParams(Part.toolRequest(toolReq), tool)); + assertEquals("weatherTool", resp.getToolResponse().getName()); } @Test @@ -290,7 +291,7 @@ void testToolNext_exceptionPropagates() { ToolRequest toolReq = new ToolRequest("t", Map.of()); assertThrows( GenkitException.class, - () -> failing.apply(ctx, new ToolParams(toolReq, createTestTool("t")))); + () -> failing.apply(ctx, new ToolParams(Part.toolRequest(toolReq), createTestTool("t")))); } // ========================================================================= @@ -325,11 +326,13 @@ public GenerationMiddleware newInstance() { assertSame(expected, mResult); // wrapTool passes through - ToolResponse toolExpected = new ToolResponse("t", "data"); + Part toolExpected = Part.toolResponse(new ToolResponse("t", "data")); ToolNext tNext = (c, p) -> toolExpected; - ToolResponse tResult = + Part tResult = base.wrapTool( - ctx, new ToolParams(new ToolRequest("t", Map.of()), createTestTool("t")), tNext); + ctx, + new ToolParams(Part.toolRequest(new ToolRequest("t", Map.of())), createTestTool("t")), + tNext); assertSame(toolExpected, tResult); // tools returns empty @@ -534,10 +537,10 @@ public GenerationMiddleware newInstance() { } @Override - public ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next) + public Part wrapTool(ActionContext ctx, ToolParams params, ToolNext next) throws GenkitException { order.add("first-before"); - ToolResponse resp = next.apply(ctx, params); + Part resp = next.apply(ctx, params); order.add("first-after"); return resp; } @@ -556,10 +559,10 @@ public GenerationMiddleware newInstance() { } @Override - public ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next) + public Part wrapTool(ActionContext ctx, ToolParams params, ToolNext next) throws GenkitException { order.add("second-before"); - ToolResponse resp = next.apply(ctx, params); + Part resp = next.apply(ctx, params); order.add("second-after"); return resp; } @@ -569,7 +572,7 @@ public ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next ToolNext core = (c, p) -> { order.add("tool"); - return new ToolResponse(p.getRequest().getName(), "output"); + return Part.toolResponse(new ToolResponse(p.getRequest().getName(), "output")); }; ToolNext chain = core; @@ -580,7 +583,7 @@ public ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next } ToolRequest toolReq = new ToolRequest("myTool", Map.of()); - chain.apply(ctx, new ToolParams(toolReq, createTestTool("myTool"))); + chain.apply(ctx, new ToolParams(Part.toolRequest(toolReq), createTestTool("myTool"))); assertEquals( List.of("first-before", "second-before", "tool", "second-after", "first-after"), order); diff --git a/genkit/src/main/java/com/google/genkit/Genkit.java b/genkit/src/main/java/com/google/genkit/Genkit.java index e4dc9ac97..73dd4d6ff 100644 --- a/genkit/src/main/java/com/google/genkit/Genkit.java +++ b/genkit/src/main/java/com/google/genkit/Genkit.java @@ -879,7 +879,8 @@ private ToolExecutionResult executeToolsWithMiddleware( Tool typedTool = (Tool) tool; Object result = typedTool.run(actx, toolInput); - return new ToolResponse(toolReq.getRef(), toolReq.getName(), result); + return Part.toolResponse( + new ToolResponse(toolReq.getRef(), toolReq.getName(), result)); }); List responseParts = new java.util.ArrayList<>(); @@ -905,14 +906,12 @@ private ToolExecutionResult executeToolsWithMiddleware( try { // Execute through WrapTool chain - ToolParams tparams = new ToolParams(toolRequest, tool); - ToolResponse toolResponse = wrappedToolCall.apply(ctx, tparams); + ToolParams tparams = new ToolParams(toolRequestPart, tool); + Part responsePart = wrappedToolCall.apply(ctx, tparams); - Part responsePart = new Part(); - responsePart.setToolResponse(toolResponse); responseParts.add(responsePart); - pendingOutputMap.put(key, toolResponse.getOutput()); + pendingOutputMap.put(key, responsePart.getToolResponse().getOutput()); logger.debug("Executed tool '{}' successfully", toolName); diff --git a/samples/middleware-v2/src/main/java/com/google/genkit/samples/MiddlewareV2Sample.java b/samples/middleware-v2/src/main/java/com/google/genkit/samples/MiddlewareV2Sample.java index 7c6f288b5..476eb2e04 100644 --- a/samples/middleware-v2/src/main/java/com/google/genkit/samples/MiddlewareV2Sample.java +++ b/samples/middleware-v2/src/main/java/com/google/genkit/samples/MiddlewareV2Sample.java @@ -23,8 +23,8 @@ import com.google.genkit.ai.GenerateOptions; import com.google.genkit.ai.GenerationConfig; import com.google.genkit.ai.ModelResponse; +import com.google.genkit.ai.Part; import com.google.genkit.ai.Tool; -import com.google.genkit.ai.ToolResponse; import com.google.genkit.ai.middleware.BaseGenerationMiddleware; import com.google.genkit.ai.middleware.GenerateNext; import com.google.genkit.ai.middleware.GenerateParams; @@ -162,12 +162,12 @@ public GenerationMiddleware newInstance() { } @Override - public ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next) + public Part wrapTool(ActionContext ctx, ToolParams params, ToolNext next) throws GenkitException { String toolName = params.getRequest().getName(); logger.info("[tool-monitor] Executing tool: {}", toolName); long start = System.currentTimeMillis(); - ToolResponse resp = next.apply(ctx, params); + Part resp = next.apply(ctx, params); logger.info( "[tool-monitor] Tool {} completed in {}ms", toolName, System.currentTimeMillis() - start); return resp; @@ -221,7 +221,7 @@ public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext } @Override - public ToolResponse wrapTool(ActionContext ctx, ToolParams params, ToolNext next) + public Part wrapTool(ActionContext ctx, ToolParams params, ToolNext next) throws GenkitException { int call = toolCalls.incrementAndGet(); logger.info("[observability] Tool call #{}: {}", call, params.getRequest().getName()); From 26034f236550dc406b63b77a6156d197100ba8e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 09:32:56 +0000 Subject: [PATCH 04/50] chore(deps)(deps): bump com.google.cloud:google-cloud-trace Bumps [com.google.cloud:google-cloud-trace](https://github.com/googleapis/google-cloud-java) from 2.87.0 to 2.88.0. - [Release notes](https://github.com/googleapis/google-cloud-java/releases) - [Changelog](https://github.com/googleapis/google-cloud-java/blob/main/java-document-ai/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-cloud-java/commits) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-trace dependency-version: 2.88.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/firebase/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/firebase/pom.xml b/plugins/firebase/pom.xml index 1d9657445..be5c4ee05 100644 --- a/plugins/firebase/pom.xml +++ b/plugins/firebase/pom.xml @@ -39,7 +39,7 @@ 9.8.0 3.38.0 3.28.0 - 2.87.0 + 2.88.0 3.88.0 2.0.0 0.36.0 From 37266f135b19b5baf2e0979877be9146a2499ef7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 09:32:38 +0000 Subject: [PATCH 05/50] chore(deps)(deps): bump aws.sdk.version from 2.42.13 to 2.42.18 Bumps `aws.sdk.version` from 2.42.13 to 2.42.18. Updates `software.amazon.awssdk:auth` from 2.42.13 to 2.42.18 Updates `software.amazon.awssdk:http-client-spi` from 2.42.13 to 2.42.18 Updates `software.amazon.awssdk:regions` from 2.42.13 to 2.42.18 --- updated-dependencies: - dependency-name: software.amazon.awssdk:auth dependency-version: 2.42.18 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.42.18 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:regions dependency-version: 2.42.18 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- plugins/aws-bedrock/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/aws-bedrock/pom.xml b/plugins/aws-bedrock/pom.xml index 499cf1a9d..25c5befaf 100644 --- a/plugins/aws-bedrock/pom.xml +++ b/plugins/aws-bedrock/pom.xml @@ -36,7 +36,7 @@ false - 2.42.13 + 2.42.18 From 8fd5c7264ec51bcbfe570c8266a1b810e94771cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 09:32:23 +0000 Subject: [PATCH 06/50] chore(deps)(deps): bump jackson.version from 2.21.1 to 2.21.2 Bumps `jackson.version` from 2.21.1 to 2.21.2. Updates `com.fasterxml.jackson.core:jackson-databind` from 2.21.1 to 2.21.2 - [Commits](https://github.com/FasterXML/jackson/commits) Updates `com.fasterxml.jackson.core:jackson-core` from 2.21.1 to 2.21.2 - [Commits](https://github.com/FasterXML/jackson-core/compare/jackson-core-2.21.1...jackson-core-2.21.2) Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.21.1 to 2.21.2 --- updated-dependencies: - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-version: 2.21.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.fasterxml.jackson.core:jackson-core dependency-version: 2.21.2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-version: 2.21.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 42210896c..982f00c01 100644 --- a/pom.xml +++ b/pom.xml @@ -129,7 +129,7 @@ true - 2.21.1 + 2.21.2 2.21 2.0.17 1.5.32 From f68a889a301183aaf8be0d5bca93a6fdfe5f2464 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 09:32:06 +0000 Subject: [PATCH 07/50] chore(deps)(deps): bump spring-boot.version from 4.0.3 to 4.0.4 Bumps `spring-boot.version` from 4.0.3 to 4.0.4. Updates `org.springframework.boot:spring-boot-starter-web` from 4.0.3 to 4.0.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v4.0.3...v4.0.4) Updates `org.springframework.boot:spring-boot-starter-test` from 4.0.3 to 4.0.4 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v4.0.3...v4.0.4) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 4.0.4 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.springframework.boot:spring-boot-starter-test dependency-version: 4.0.4 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- plugins/spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/spring/pom.xml b/plugins/spring/pom.xml index 0684e28aa..5cfb22a04 100644 --- a/plugins/spring/pom.xml +++ b/plugins/spring/pom.xml @@ -36,7 +36,7 @@ false - 4.0.3 + 4.0.4 From 3883284ee1ab4e57d0da478b051302e97a98487d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 09:31:41 +0000 Subject: [PATCH 08/50] chore(deps)(deps): bump com.google.genai:google-genai Bumps [com.google.genai:google-genai](https://github.com/googleapis/java-genai) from 1.43.0 to 1.44.0. - [Release notes](https://github.com/googleapis/java-genai/releases) - [Changelog](https://github.com/googleapis/java-genai/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-genai/compare/v1.43.0...v1.44.0) --- updated-dependencies: - dependency-name: com.google.genai:google-genai dependency-version: 1.44.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/google-genai/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/google-genai/pom.xml b/plugins/google-genai/pom.xml index 65e20fe46..4b5a5ba30 100644 --- a/plugins/google-genai/pom.xml +++ b/plugins/google-genai/pom.xml @@ -49,7 +49,7 @@ com.google.genai google-genai - 1.43.0 + 1.44.0 From 3765fee1ff058c5e791e585b2919c616fa612f24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 09:37:39 +0000 Subject: [PATCH 09/50] chore(deps)(deps): bump com.google.cloud:google-cloud-logging Bumps [com.google.cloud:google-cloud-logging](https://github.com/googleapis/java-logging) from 3.28.0 to 3.29.0. - [Release notes](https://github.com/googleapis/java-logging/releases) - [Changelog](https://github.com/googleapis/java-logging/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-logging/commits) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-logging dependency-version: 3.29.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/firebase/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/firebase/pom.xml b/plugins/firebase/pom.xml index be5c4ee05..42028c26c 100644 --- a/plugins/firebase/pom.xml +++ b/plugins/firebase/pom.xml @@ -38,7 +38,7 @@ false 9.8.0 3.38.0 - 3.28.0 + 3.29.0 2.88.0 3.88.0 2.0.0 From b7cbb841a7c8a33f0e9ef07c68e386563a2cba40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 09:37:56 +0000 Subject: [PATCH 10/50] chore(deps)(deps): bump com.google.cloud:google-cloud-monitoring Bumps [com.google.cloud:google-cloud-monitoring](https://github.com/googleapis/google-cloud-java) from 3.88.0 to 3.89.0. - [Release notes](https://github.com/googleapis/google-cloud-java/releases) - [Changelog](https://github.com/googleapis/google-cloud-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-cloud-java/commits) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-monitoring dependency-version: 3.89.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/firebase/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/firebase/pom.xml b/plugins/firebase/pom.xml index 42028c26c..685a0d36b 100644 --- a/plugins/firebase/pom.xml +++ b/plugins/firebase/pom.xml @@ -40,7 +40,7 @@ 3.38.0 3.29.0 2.88.0 - 3.88.0 + 3.89.0 2.0.0 0.36.0 From dcde400dfb69371718d26ac70cb21bab525f742a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 09:39:31 +0000 Subject: [PATCH 11/50] chore(deps)(deps): bump com.google.genai:google-genai Bumps [com.google.genai:google-genai](https://github.com/googleapis/java-genai) from 1.44.0 to 1.45.0. - [Release notes](https://github.com/googleapis/java-genai/releases) - [Changelog](https://github.com/googleapis/java-genai/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-genai/compare/v1.44.0...v1.45.0) --- updated-dependencies: - dependency-name: com.google.genai:google-genai dependency-version: 1.45.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/google-genai/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/google-genai/pom.xml b/plugins/google-genai/pom.xml index 4b5a5ba30..b66d5da6b 100644 --- a/plugins/google-genai/pom.xml +++ b/plugins/google-genai/pom.xml @@ -49,7 +49,7 @@ com.google.genai google-genai - 1.44.0 + 1.45.0 From 0a8a296b18e3dc7ad90088b8e102aeda6b46e2b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 09:39:14 +0000 Subject: [PATCH 12/50] chore(deps)(deps): bump com.puppycrawl.tools:checkstyle Bumps [com.puppycrawl.tools:checkstyle](https://github.com/checkstyle/checkstyle) from 13.3.0 to 13.4.0. - [Release notes](https://github.com/checkstyle/checkstyle/releases) - [Commits](https://github.com/checkstyle/checkstyle/compare/checkstyle-13.3.0...checkstyle-13.4.0) --- updated-dependencies: - dependency-name: com.puppycrawl.tools:checkstyle dependency-version: 13.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 982f00c01..6ad171b59 100644 --- a/pom.xml +++ b/pom.xml @@ -142,7 +142,7 @@ 5.3.2 2.29 1.35.0 - 13.3.0 + 13.4.0 From 2d757699c9b2d743b415db5eb33bf2d1900ec980 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 09:38:49 +0000 Subject: [PATCH 13/50] chore(deps)(deps): bump google-cloud-firestore.version Bumps `google-cloud-firestore.version` from 3.38.0 to 3.39.0. Updates `com.google.cloud:google-cloud-firestore` from 3.38.0 to 3.39.0 - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.38.0...v3.39.0) Updates `com.google.cloud:google-cloud-firestore-admin` from 3.38.0 to 3.39.0 - [Release notes](https://github.com/googleapis/java-firestore/releases) - [Changelog](https://github.com/googleapis/java-firestore/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-firestore/compare/v3.38.0...v3.39.0) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-firestore dependency-version: 3.39.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.google.cloud:google-cloud-firestore-admin dependency-version: 3.39.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/firebase/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/firebase/pom.xml b/plugins/firebase/pom.xml index 685a0d36b..cbcff0ef5 100644 --- a/plugins/firebase/pom.xml +++ b/plugins/firebase/pom.xml @@ -37,7 +37,7 @@ false 9.8.0 - 3.38.0 + 3.39.0 3.29.0 2.88.0 3.89.0 From f04958f221a5ba209060c3b42c500057dd18a127 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 09:38:37 +0000 Subject: [PATCH 14/50] chore(deps)(deps): bump aws.sdk.version from 2.42.18 to 2.42.23 Bumps `aws.sdk.version` from 2.42.18 to 2.42.23. Updates `software.amazon.awssdk:auth` from 2.42.18 to 2.42.23 Updates `software.amazon.awssdk:http-client-spi` from 2.42.18 to 2.42.23 Updates `software.amazon.awssdk:regions` from 2.42.18 to 2.42.23 --- updated-dependencies: - dependency-name: software.amazon.awssdk:auth dependency-version: 2.42.23 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.42.23 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:regions dependency-version: 2.42.23 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- plugins/aws-bedrock/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/aws-bedrock/pom.xml b/plugins/aws-bedrock/pom.xml index 25c5befaf..1b4182401 100644 --- a/plugins/aws-bedrock/pom.xml +++ b/plugins/aws-bedrock/pom.xml @@ -36,7 +36,7 @@ false - 2.42.18 + 2.42.23 From 15567a45e02851c1abffa0fc01087a40910887be Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 09:39:06 +0000 Subject: [PATCH 15/50] chore(deps)(deps): bump spring-boot.version from 4.0.4 to 4.0.5 Bumps `spring-boot.version` from 4.0.4 to 4.0.5. Updates `org.springframework.boot:spring-boot-starter-web` from 4.0.4 to 4.0.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v4.0.4...v4.0.5) Updates `org.springframework.boot:spring-boot-starter-test` from 4.0.4 to 4.0.5 - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v4.0.4...v4.0.5) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-starter-web dependency-version: 4.0.5 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.springframework.boot:spring-boot-starter-test dependency-version: 4.0.5 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- plugins/spring/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/spring/pom.xml b/plugins/spring/pom.xml index 5cfb22a04..84d1200b1 100644 --- a/plugins/spring/pom.xml +++ b/plugins/spring/pom.xml @@ -36,7 +36,7 @@ false - 4.0.4 + 4.0.5 From 3df5dc9ce554a81a627210d59659b2ddf644df53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 09:39:26 +0000 Subject: [PATCH 16/50] chore(deps)(deps): bump io.modelcontextprotocol.sdk:mcp Bumps [io.modelcontextprotocol.sdk:mcp](https://github.com/modelcontextprotocol/java-sdk) from 1.1.0 to 1.1.1. - [Release notes](https://github.com/modelcontextprotocol/java-sdk/releases) - [Commits](https://github.com/modelcontextprotocol/java-sdk/compare/v1.1.0...v1.1.1) --- updated-dependencies: - dependency-name: io.modelcontextprotocol.sdk:mcp dependency-version: 1.1.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- plugins/mcp/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mcp/pom.xml b/plugins/mcp/pom.xml index 06046c92e..d055444c6 100644 --- a/plugins/mcp/pom.xml +++ b/plugins/mcp/pom.xml @@ -35,7 +35,7 @@ Model Context Protocol (MCP) plugin for Genkit - enables integration with MCP servers and tools - 1.1.0 + 1.1.1 false From 6c9bb8ec106ec2430c545da3cfd860e3fd6aab02 Mon Sep 17 00:00:00 2001 From: xavidop Date: Mon, 30 Mar 2026 17:28:49 +0200 Subject: [PATCH 17/50] feat: new main page --- .github/workflows/publish-docs.yml | 88 ++++++ .github/workflows/publish-javadoc.yml | 49 ---- .gitignore | 1 + docs/astro.config.mjs | 142 ++++++++++ docs/package.json | 20 ++ docs/public/favicon.ico | Bin 0 -> 15086 bytes docs/src/assets/logo-dark.svg | 18 ++ docs/src/assets/logo-light.svg | 18 ++ docs/src/content.config.ts | 7 + docs/src/content/docs/404.md | 9 + docs/src/content/docs/architecture.md | 92 +++++++ docs/src/content/docs/chat-sessions.md | 244 ++++++++++++++++ docs/src/content/docs/dev-ui.md | 55 ++++ docs/src/content/docs/dotprompt.md | 64 +++++ docs/src/content/docs/evaluations.md | 259 +++++++++++++++++ docs/src/content/docs/flows.mdx | 72 +++++ docs/src/content/docs/getting-started.mdx | 153 +++++++++++ docs/src/content/docs/index.mdx | 260 ++++++++++++++++++ docs/src/content/docs/interrupts.md | 192 +++++++++++++ docs/src/content/docs/middleware.md | 230 ++++++++++++++++ docs/src/content/docs/models.mdx | 117 ++++++++ docs/src/content/docs/multi-agent.md | 155 +++++++++++ docs/src/content/docs/observability.md | 86 ++++++ docs/src/content/docs/overview.mdx | 59 ++++ docs/src/content/docs/plugins/anthropic.md | 63 +++++ docs/src/content/docs/plugins/aws-bedrock.md | 62 +++++ .../src/content/docs/plugins/azure-foundry.md | 52 ++++ docs/src/content/docs/plugins/cohere.md | 51 ++++ docs/src/content/docs/plugins/compat-oai.md | 48 ++++ docs/src/content/docs/plugins/deepseek.md | 49 ++++ docs/src/content/docs/plugins/evaluators.md | 62 +++++ .../docs/plugins/firebase-functions.md | 123 +++++++++ .../docs/plugins/firebase-vector-store.md | 123 +++++++++ docs/src/content/docs/plugins/firebase.md | 49 ++++ docs/src/content/docs/plugins/google-genai.md | 172 ++++++++++++ docs/src/content/docs/plugins/groq.md | 53 ++++ docs/src/content/docs/plugins/jetty.md | 48 ++++ docs/src/content/docs/plugins/localvec.md | 38 +++ docs/src/content/docs/plugins/mcp.md | 235 ++++++++++++++++ docs/src/content/docs/plugins/mistral.md | 54 ++++ docs/src/content/docs/plugins/ollama.md | 68 +++++ docs/src/content/docs/plugins/openai.md | 67 +++++ .../src/content/docs/plugins/opentelemetry.md | 151 ++++++++++ docs/src/content/docs/plugins/overview.mdx | 73 +++++ docs/src/content/docs/plugins/pinecone.md | 42 +++ docs/src/content/docs/plugins/postgresql.md | 43 +++ docs/src/content/docs/plugins/spring.md | 60 ++++ docs/src/content/docs/plugins/weaviate.md | 47 ++++ docs/src/content/docs/plugins/xai.md | 55 ++++ docs/src/content/docs/rag.mdx | 77 ++++++ docs/src/content/docs/samples.md | 65 +++++ docs/src/content/docs/streaming.md | 35 +++ docs/src/content/docs/structured-output.md | 88 ++++++ docs/src/content/docs/tools.mdx | 81 ++++++ docs/src/styles/custom.css | 60 ++++ docs/tsconfig.json | 3 + 56 files changed, 4638 insertions(+), 49 deletions(-) create mode 100644 .github/workflows/publish-docs.yml delete mode 100644 .github/workflows/publish-javadoc.yml create mode 100644 docs/astro.config.mjs create mode 100644 docs/package.json create mode 100644 docs/public/favicon.ico create mode 100644 docs/src/assets/logo-dark.svg create mode 100644 docs/src/assets/logo-light.svg create mode 100644 docs/src/content.config.ts create mode 100644 docs/src/content/docs/404.md create mode 100644 docs/src/content/docs/architecture.md create mode 100644 docs/src/content/docs/chat-sessions.md create mode 100644 docs/src/content/docs/dev-ui.md create mode 100644 docs/src/content/docs/dotprompt.md create mode 100644 docs/src/content/docs/evaluations.md create mode 100644 docs/src/content/docs/flows.mdx create mode 100644 docs/src/content/docs/getting-started.mdx create mode 100644 docs/src/content/docs/index.mdx create mode 100644 docs/src/content/docs/interrupts.md create mode 100644 docs/src/content/docs/middleware.md create mode 100644 docs/src/content/docs/models.mdx create mode 100644 docs/src/content/docs/multi-agent.md create mode 100644 docs/src/content/docs/observability.md create mode 100644 docs/src/content/docs/overview.mdx create mode 100644 docs/src/content/docs/plugins/anthropic.md create mode 100644 docs/src/content/docs/plugins/aws-bedrock.md create mode 100644 docs/src/content/docs/plugins/azure-foundry.md create mode 100644 docs/src/content/docs/plugins/cohere.md create mode 100644 docs/src/content/docs/plugins/compat-oai.md create mode 100644 docs/src/content/docs/plugins/deepseek.md create mode 100644 docs/src/content/docs/plugins/evaluators.md create mode 100644 docs/src/content/docs/plugins/firebase-functions.md create mode 100644 docs/src/content/docs/plugins/firebase-vector-store.md create mode 100644 docs/src/content/docs/plugins/firebase.md create mode 100644 docs/src/content/docs/plugins/google-genai.md create mode 100644 docs/src/content/docs/plugins/groq.md create mode 100644 docs/src/content/docs/plugins/jetty.md create mode 100644 docs/src/content/docs/plugins/localvec.md create mode 100644 docs/src/content/docs/plugins/mcp.md create mode 100644 docs/src/content/docs/plugins/mistral.md create mode 100644 docs/src/content/docs/plugins/ollama.md create mode 100644 docs/src/content/docs/plugins/openai.md create mode 100644 docs/src/content/docs/plugins/opentelemetry.md create mode 100644 docs/src/content/docs/plugins/overview.mdx create mode 100644 docs/src/content/docs/plugins/pinecone.md create mode 100644 docs/src/content/docs/plugins/postgresql.md create mode 100644 docs/src/content/docs/plugins/spring.md create mode 100644 docs/src/content/docs/plugins/weaviate.md create mode 100644 docs/src/content/docs/plugins/xai.md create mode 100644 docs/src/content/docs/rag.mdx create mode 100644 docs/src/content/docs/samples.md create mode 100644 docs/src/content/docs/streaming.md create mode 100644 docs/src/content/docs/structured-output.md create mode 100644 docs/src/content/docs/tools.mdx create mode 100644 docs/src/styles/custom.css create mode 100644 docs/tsconfig.json diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml new file mode 100644 index 000000000..56b5415ce --- /dev/null +++ b/.github/workflows/publish-docs.yml @@ -0,0 +1,88 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +name: Publish Docs & Javadoc + +on: + push: + branches: + - main + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: "21" + distribution: "temurin" + cache: "maven" + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + cache-dependency-path: docs/package-lock.json + + - name: Generate Javadoc + run: mvn javadoc:aggregate -q + + - name: Install docs dependencies + working-directory: docs + run: npm install + + - name: Build docs site + working-directory: docs + run: npm run build + + - name: Copy Javadoc into docs output + run: | + mkdir -p docs/dist/javadoc + cp -r target/javadoc/javadoc/* docs/dist/javadoc/ 2>/dev/null || \ + cp -r target/javadoc/apidocs/* docs/dist/javadoc/ 2>/dev/null || \ + cp -r target/site/apidocs/* docs/dist/javadoc/ 2>/dev/null || true + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/dist + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/publish-javadoc.yml b/.github/workflows/publish-javadoc.yml deleted file mode 100644 index c2447747d..000000000 --- a/.github/workflows/publish-javadoc.yml +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright 2025 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 - -name: Publish Javadoc - -on: - push: - branches: - - main - -jobs: - publish: - runs-on: ubuntu-latest - permissions: - contents: write - - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Set up JDK 21 - uses: actions/setup-java@v5 - with: - java-version: '21' - distribution: 'temurin' - cache: 'maven' - - - name: Generate Javadoc - run: mvn javadoc:aggregate - - - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v4 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./target/javadoc/apidocs - destination_dir: ./ diff --git a/.gitignore b/.gitignore index 37a71bf45..35a6456fe 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ .env.local .env.*.local .angular +.astro .next .pnpm-store .vscode diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs new file mode 100644 index 000000000..3e3a93aeb --- /dev/null +++ b/docs/astro.config.mjs @@ -0,0 +1,142 @@ +import { defineConfig } from "astro/config"; +import starlight from "@astrojs/starlight"; + +export default defineConfig({ + site: "https://genkit-ai.github.io/genkit-java", + base: "/genkit-java", + integrations: [ + starlight({ + title: "Java (Unofficial)", + favicon: 'favicon.ico', + description: + "The Java implementation of the Genkit framework for building AI-powered applications.", + social: [ + { + icon: "github", + label: "GitHub", + href: "https://github.com/genkit-ai/genkit-java", + }, + { + icon: "discord", + label: "Discord", + href: "https://discord.gg/qXt5zzQKpc", + }, + { + icon: "x.com", + label: "X", + href: "https://x.com/GenkitFramework", + }, + { + icon: "linkedin", + label: "LinkedIn", + href: "https://www.linkedin.com/company/genkit", + }, + ], + logo: { + alt: "Genkit Java", + dark: "./src/assets/logo-dark.svg", + light: "./src/assets/logo-light.svg", + }, + customCss: ["./src/styles/custom.css"], + editLink: { + baseUrl: + "https://github.com/genkit-ai/genkit-java/edit/main/docs/", + }, + sidebar: [ + { + label: "Getting Started", + items: [ + { label: "Overview", slug: "overview" }, + { label: "Get Started", slug: "getting-started" }, + ], + }, + { + label: "Building AI Features", + items: [ + { label: "Generating Content", slug: "models" }, + { label: "Creating Flows", slug: "flows" }, + { label: "Tool Calling", slug: "tools" }, + { label: "DotPrompt", slug: "dotprompt" }, + { label: "Structured Output", slug: "structured-output" }, + { label: "Streaming", slug: "streaming" }, + { label: "RAG", slug: "rag" }, + { label: "Chat Sessions", slug: "chat-sessions" }, + { label: "Evaluations", slug: "evaluations" }, + { label: "Middleware", slug: "middleware" }, + { label: "Interrupts", slug: "interrupts" }, + { label: "Multi-Agent", slug: "multi-agent" }, + ], + }, + { + label: "Observability", + items: [ + { label: "Overview", slug: "observability" }, + { label: "OpenTelemetry Plugin", slug: "plugins/opentelemetry" }, + ], + }, + { + label: "Model Plugins", + items: [ + { label: "Overview", slug: "plugins/overview" }, + { label: "Google GenAI (Gemini)", slug: "plugins/google-genai" }, + { label: "OpenAI", slug: "plugins/openai" }, + { label: "Anthropic (Claude)", slug: "plugins/anthropic" }, + { label: "AWS Bedrock", slug: "plugins/aws-bedrock" }, + { label: "Azure AI Foundry", slug: "plugins/azure-foundry" }, + { label: "xAI (Grok)", slug: "plugins/xai" }, + { label: "DeepSeek", slug: "plugins/deepseek" }, + { label: "Cohere", slug: "plugins/cohere" }, + { label: "Mistral", slug: "plugins/mistral" }, + { label: "Groq", slug: "plugins/groq" }, + { label: "Ollama", slug: "plugins/ollama" }, + { label: "OpenAI-Compatible", slug: "plugins/compat-oai" }, + ], + }, + { + label: "Server & Deployment", + items: [ + { label: "Jetty Server", slug: "plugins/jetty" }, + { label: "Spring Boot", slug: "plugins/spring" }, + { label: "Firebase Functions", slug: "plugins/firebase-functions" }, + ], + }, + { + label: "Vector Databases", + items: [ + { label: "Firebase (Firestore)", slug: "plugins/firebase-vector-store" }, + { label: "Local Vector Store", slug: "plugins/localvec" }, + { label: "Weaviate", slug: "plugins/weaviate" }, + { label: "PostgreSQL (pgvector)", slug: "plugins/postgresql" }, + { label: "Pinecone", slug: "plugins/pinecone" }, + ], + }, + { + label: "Other Plugins", + items: [ + { label: "Firebase", slug: "plugins/firebase" }, + { label: "MCP", slug: "plugins/mcp" }, + { label: "Evaluators", slug: "plugins/evaluators" }, + ], + }, + { + label: "Development", + items: [ + { label: "Dev UI & CLI", slug: "dev-ui" }, + { label: "Architecture", slug: "architecture" }, + { label: "Samples", slug: "samples" }, + ], + }, + { + label: "API Reference", + items: [ + { + label: "Javadoc", + link: "/genkit-java/javadoc/", + attrs: { target: "_blank" }, + }, + ], + }, + ], + }), + ], +}); diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 000000000..702cd13b2 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,20 @@ +{ + "name": "genkit-java-docs", + "type": "module", + "version": "0.0.1", + "scripts": { + "dev": "astro dev", + "start": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@astrojs/starlight": "^0.34.0", + "astro": "5.17.3", + "sharp": "^0.33.0" + }, + "overrides": { + "@astrojs/sitemap": "3.6.1" + } +} diff --git a/docs/public/favicon.ico b/docs/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..77e376551dc0754725dd46a4097bae3dc4d8ebec GIT binary patch literal 15086 zcmds;2ap!U5`dSCC|N*2;K(^C4?#e3j#5BjK>?CcB>2cYl4K}~sAQB>gi?tQ6(~3q zm7wHMP=Y5pNG@{dH{aCF|MvFw-#;D6sT%gb-I?y5o}P|_WhJtbSfxr?>KSD%&u>{} zEz63Eiv2sCWt~*-(y1rkyC$`)F={{|HAYRb+NsX_1Zou&oPVpnMD_iuUshcr?7#pP zFo6w@glvj4R!7y>sD4NFNa+nU9~^5{2Pe2AX-(AR^{NNL`Lw=+8yZ4wtouKzpH=;7 z(H1P+bFs7pyB8VM_^B@d-Olwv6SR3X*7zdQZq@>AUi*N|n6WSF6B^xG6ZH~V3!Cl~ zDN;zbY}uqkhYqrE;X>K9YnN=>woQf(9V+j<^G?`s9W-$!q1E2llZf<8u3WjKPMtdP z>8GE{>eZ_yCMHInJb5B_@7|SPfBjV^O`0T4n>LlAMT<(hbm<~p3$)th2wfX8%;d?F zOZxQbC1=i@^5KUc%8C^$)mu6`D=MUW5!MZ{EDpzI}Vm@8idh%Z(d1s9j~*?>ix-#IUVANMys@;di6tI;9kg9dmMocM z$dExAHf$*4$B);vK$GG3{{8!9=FFMWxpQZ!RjZaXY0^Z-j2R<8{`jM&pLJZneq9b6 zI3Qnr^_6_|(MS4hyz67ul z{`yNcZrmtCh76H1Wy(nE)Tw38RH%AtiNdkpj>q}?_19mQDpjgz`p}1e|NXbd&3W9td$)Z4 z`RCH8Q6sHa80(yG{$>wZL-y?1<-PablU}`gY2AAH@@3!I;LNODyH@)5@2_JepRL=+VP>)|M<;;(12x@R6&} zKKo2+)TkkEyzxd*d_x}_JMzsp-{`q^nBn{mhko=-mo8ld-8gyjWT{Z0LQuN>MWMfZ z`SLP%?p!U)=vMSD=OGaPo;Us2tP&+kXkCjgH2xbtDEg8$c%7N&K|lHh`F{KDx2099 zRx)kcG&y$cnC87XIM@nwB5SB!ySC)dpI`euPT%cW(~oY4r}*~R9H(tI^z+0{U%7Hc z=FOWY&6_uu9654mduErZXH7r$DO09Qg1qYR*baQZf&~i-@y_$4 zA6drsX3d&4DET3_L9e3g@nM~3;aT(F&O3ga^Na4m->{$GK7IOVyXWM&9meyf-_U{| zimfxg>C2)&4gL6`ojP@rAAa~j1`Qe{Z@u+aJXtYg?a!yp|F6IPTKmd&oc?~}-whZr zK%a|X<8@+uVj|+ugz_J{ckiy_Z)6!e)U#(#t#j~!-F#!S9{ft;^gwnO`;jJ18hxhm z$uC^Epy?+UP_0_EIQq%J4&_OF4orM=atXiv_M6Tj5EHTnf3V0)QloUcD+`eDQ_W%U*M1zj?+0_`vh$&(}Ew*1+EG-Md%kr+WA9t@WWBkFgEp z7MwN~oU9xB_5S)0N<2VCG29}#1;Y}r!BtO4jZwFAF=M7P(gS5LnD@=F~r!Xp~? zh+G4FB7Q7Tpg^3M-HF#3KN44b`|Y=K>eQ(?@#A;jeJ3MEjL`8}kntliuwVN51`m-@ zctQ@r-i(eU_QrSf2g}4M#Ej5rZ{&rr7h%Tl%whoJ%tE*yZHua{U^H<0&IfNE1T4-Me zUXouUf5rJGews69j&$tUQL0z3u6WkAfdh5UC4&4RIZxBV%HJYWY15|F zc{AcgY>v?*_wL=(zu6bo#CazcgYL3r%L+CcU&u{ADWZbmr9FQecK)2Rfv(JxCy$Oz zh*6d=UoJfVBfP zCYUuC9~hX%pYcn8^Mk}#7Fie&@y`>U!z}9_&s>&uoo8Chis6~!|IcXtPGVWZc_xbg zx|~T;zJBi8-EPn3?B_&4WA9)xu+GSdKJ~fyr-2zP3@xkQq>Qr4vJ7Z zbC?SZUIMOxhQ;p^3K$GiC?%6K~9NUJASu<8K{HPoH4&+fyLUmA9C>EL5+*}me`8> zJmmMu{Xhfp6nA8Z=czL~^T;kVne!bFKYL8vOZ-E9lX!tRly#D$Am%2o}e`jXVtZ zQHb;X#AB{{jk`qT1WjzmU4XiE>&D9gK&PFj@SEHkIoj{P|6bEg&9-mfzVYVT=Q)3q zUnFOB`0!zUUxl+q4CEX~eZ={Wzu`}@V#V~Huy4o&cY55$y1nCG1PvZg|8g6{JMwqj zrSbQjyEfF`iPs7hD&%*ZbAB-RkpuD`+~4t+D|lM?(Rt(&$z6~KAfMqzLqg+64~!f+ zQlANMn})0$I&?_qhNznvekLS-WSw<$204><{P2K%An!^I>uKOOFnK};c`n2Ml`B`e z-bF~r_1p3D8@WgKle2Q$d&WeHA6;9nTsa>heMu^}%V>iHu#kN!My;)Jek zlh1S0pVTa!H1Rh!3pf^@#G;Z8j7A#nx zX*Tv}!h{K$7jFEE#f=S&vkv}dd@faM!v2!uCI5*1C>lco5I;gKEaDGhW?`_hR+hB|E0*(v4Nq3>7R^Nc^hPlJY_;}A90ZEarffbEQZ)T=m+ zyZ_NT+&~>Otly%BZ1JR6nb_+q!wjH^wm5-rA`@j;ii&p7T9p znL`6syt(-M*ahkIx zogwbzqW_2)xVMY_#Xcz9bRVy!u~Uh}yh z0sVH`pdTA*=+BogpI`b}8?h+&p71Hq#b)23opo_fiZwcX4!sAA;SLP@#QwoI^f!CK z{}sW{VE>3g4Sy9~MxK5AV9$v&iE**TKmYu*hDQwSa_`%mg*fsHeEfA}ftcRUy>27F riVS@g;=q7s9-Xo6RJS*C7JM?PK00BT_4>|lw917J4?`<4hq?a)@rY34 literal 0 HcmV?d00001 diff --git a/docs/src/assets/logo-dark.svg b/docs/src/assets/logo-dark.svg new file mode 100644 index 000000000..291651689 --- /dev/null +++ b/docs/src/assets/logo-dark.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/docs/src/assets/logo-light.svg b/docs/src/assets/logo-light.svg new file mode 100644 index 000000000..2c30a9c76 --- /dev/null +++ b/docs/src/assets/logo-light.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/docs/src/content.config.ts b/docs/src/content.config.ts new file mode 100644 index 000000000..7fbcf2c33 --- /dev/null +++ b/docs/src/content.config.ts @@ -0,0 +1,7 @@ +import { defineCollection } from "astro:content"; +import { docsLoader } from "@astrojs/starlight/loaders"; +import { docsSchema } from "@astrojs/starlight/schema"; + +export const collections = { + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), +}; diff --git a/docs/src/content/docs/404.md b/docs/src/content/docs/404.md new file mode 100644 index 000000000..a5558c702 --- /dev/null +++ b/docs/src/content/docs/404.md @@ -0,0 +1,9 @@ +--- +title: '404' +template: splash +editUrl: false +hero: + tagline: Page not found + actions: [] +pagefind: false +--- diff --git a/docs/src/content/docs/architecture.md b/docs/src/content/docs/architecture.md new file mode 100644 index 000000000..c9d4cd584 --- /dev/null +++ b/docs/src/content/docs/architecture.md @@ -0,0 +1,92 @@ +--- +title: Architecture +description: Understand the architecture of Genkit for Java. +--- + +## Module structure + +``` +com.google.genkit +├── core/ # Core framework +│ ├── Action # Base action interface +│ ├── ActionDef # Action implementation +│ ├── ActionContext # Execution context with registry access +│ ├── Flow # Flow definition +│ ├── Registry # Action registry +│ ├── Plugin # Plugin interface +│ └── tracing/ # OpenTelemetry integration +│ ├── Tracer # Span management +│ └── TelemetryClient # Telemetry export +├── ai/ # AI features +│ ├── Model # Model interface +│ ├── ModelRequest/Response# Model I/O types +│ ├── Tool # Tool definition +│ ├── Embedder # Embedder interface +│ ├── Retriever # Retriever interface +│ ├── Indexer # Indexer interface +│ ├── Prompt # Prompt templates +│ ├── telemetry/ # AI-specific metrics +│ └── evaluation/ # Evaluation framework +├── genkit/ # Main module +│ ├── Genkit # Main entry point & builder +│ ├── GenkitOptions # Configuration options +│ ├── ReflectionServer # Dev UI integration +│ └── prompt/ # DotPrompt support +└── plugins/ # Plugin implementations + ├── openai/ # OpenAI models & embeddings + ├── google-genai/ # Google Gemini & Imagen + ├── anthropic/ # Anthropic Claude + ├── aws-bedrock/ # AWS Bedrock + ├── azure-foundry/ # Azure AI Foundry + ├── xai/ # xAI Grok + ├── deepseek/ # DeepSeek + ├── cohere/ # Cohere Command + ├── mistral/ # Mistral AI + ├── groq/ # Groq inference + ├── ollama/ # Local Ollama + ├── compat-oai/ # OpenAI-compatible base + ├── jetty/ # Jetty HTTP server + ├── spring/ # Spring Boot server + ├── localvec/ # Local vector store + ├── mcp/ # Model Context Protocol + ├── firebase/ # Firebase integration + ├── weaviate/ # Weaviate vector DB + ├── postgresql/ # PostgreSQL pgvector + ├── pinecone/ # Pinecone vector DB + └── evaluators/ # Pre-built evaluators +``` + +## Key concepts + +### Actions + +Actions are the fundamental building blocks. Every model call, tool invocation, flow execution, and embedding operation is an action. Actions provide: + +- Consistent input/output typing +- Automatic tracing with OpenTelemetry +- Registration in the Genkit registry +- Discoverability via the Dev UI + +### Registry + +The registry tracks all registered actions (flows, models, tools, prompts, retrievers, indexers, evaluators). It enables: + +- Dev UI discovery +- CLI integration +- Plugin-based extensibility + +### Plugins + +Plugins register actions with the Genkit registry. They implement the `Plugin` interface and add models, embedders, tools, or other capabilities. + +```java +JettyPlugin jetty = new JettyPlugin(options); + +Genkit genkit = Genkit.builder() + .plugin(OpenAIPlugin.create()) + .plugin(jetty) + .build(); + +// Start the server after defining flows +jetty.start(); +``` diff --git a/docs/src/content/docs/chat-sessions.md b/docs/src/content/docs/chat-sessions.md new file mode 100644 index 000000000..d89c14001 --- /dev/null +++ b/docs/src/content/docs/chat-sessions.md @@ -0,0 +1,244 @@ +--- +title: Chat Sessions +description: Build multi-turn chat experiences with session persistence. +--- + +Genkit supports multi-turn chat sessions with automatic history management, typed session state, multiple conversation threads, and pluggable persistence via `SessionStore`. + +## Creating a session + +```java +Session session = genkit.createSession(); +``` + +### With session options + +```java +import com.google.genkit.ai.session.*; + +Session session = genkit.createSession( + SessionOptions.builder() + .store(sessionStore) + .initialState(new ConversationState("John")) + .sessionId("custom-session-id") // optional, auto-generated if omitted + .build()); +``` + +## Starting a chat + +Create a `Chat` from a session. The chat manages message history, tool execution, and streaming automatically: + +```java +Chat chat = session.chat( + ChatOptions.builder() + .model("openai/gpt-4o-mini") + .system("You are a helpful assistant.") + .tools(List.of(noteTool, searchTool)) + .config(GenerationConfig.builder().temperature(0.7).build()) + .build()); +``` + +## Sending messages + +Each `send()` call automatically includes the full conversation history: + +```java +ModelResponse r1 = chat.send("What is the capital of France?"); +System.out.println(r1.getText()); // "Paris" + +ModelResponse r2 = chat.send("And what about Germany?"); +System.out.println(r2.getText()); // "Berlin" — knows we're asking about capitals +``` + +### With per-message options + +Override model or tools for a specific message: + +```java +ModelResponse response = chat.send("Summarize everything", + Chat.SendOptions.builder() + .model("openai/gpt-4o") // use a different model for this turn + .maxTurns(5) + .build()); +``` + +### Streaming + +```java +ModelResponse response = chat.sendStream("Tell me a long story", + (chunk) -> System.out.print(chunk.getText())); +``` + +## Session state + +Sessions can hold typed state that persists across messages and is saved to the store: + +```java +public class ConversationState { + private String userName; + private int messageCount; + private List topics; + // constructors, getters, setters... +} + +// Read state +ConversationState state = session.getState(); +System.out.println("User: " + state.getUserName()); + +// Update state +state.incrementMessageCount(); +state.getTopics().add("geography"); +session.updateState(state).join(); +``` + +## Conversation threads + +A single session can have multiple independent conversation threads, each with its own history: + +```java +Chat generalChat = session.chat("general", + ChatOptions.builder() + .model("openai/gpt-4o-mini") + .system("You are a general assistant.") + .build()); + +Chat supportChat = session.chat("support", + ChatOptions.builder() + .model("openai/gpt-4o-mini") + .system("You are a support agent.") + .build()); + +// Each thread maintains separate history +generalChat.send("What's the weather like?"); +supportChat.send("I have a billing issue"); +``` + +### Accessing history + +```java +List history = chat.getHistory(); +List supportHistory = session.getMessages("support"); +List mainHistory = session.getMessages(); // default "main" thread +``` + +## Loading existing sessions + +Resume a previous session by loading it from the store: + +```java +Session loaded = genkit.loadSession( + "session-123", + SessionOptions.builder() + .store(sessionStore) + .build() +).get(); // returns CompletableFuture + +// Continue the conversation with full history +Chat chat = loaded.chat(); +chat.send("Where were we?"); +``` + +## SessionStore + +The `SessionStore` interface defines how sessions are persisted. Genkit includes an in-memory implementation, and you can create custom stores for any backend. + +### Interface + +```java +public interface SessionStore { + CompletableFuture> get(String sessionId); + CompletableFuture save(String sessionId, SessionData sessionData); + CompletableFuture delete(String sessionId); +} +``` + +### InMemorySessionStore + +The default implementation stores sessions in a `ConcurrentHashMap`. Useful for development and testing — data is lost when the process stops: + +```java +InMemorySessionStore store = new InMemorySessionStore<>(); + +Session session = genkit.createSession( + SessionOptions.builder() + .store(store) + .initialState(new ConversationState("John")) + .build()); +``` + +### Creating a custom SessionStore + +Implement the `SessionStore` interface to persist sessions to any backend — Redis, a database, Firebase, etc. + +```java +import com.google.genkit.ai.session.SessionStore; +import com.google.genkit.ai.session.SessionData; + +public class RedisSessionStore implements SessionStore { + private final RedisClient redis; + private final ObjectMapper mapper; + + public RedisSessionStore(RedisClient redis, ObjectMapper mapper) { + this.redis = redis; + this.mapper = mapper; + } + + @Override + public CompletableFuture> get(String sessionId) { + return CompletableFuture.supplyAsync(() -> { + String json = redis.get("session:" + sessionId); + if (json == null) return null; + return mapper.readValue(json, SessionData.class); + }); + } + + @Override + public CompletableFuture save(String sessionId, SessionData data) { + return CompletableFuture.runAsync(() -> { + String json = mapper.writeValueAsString(data); + redis.set("session:" + sessionId, json); + }); + } + + @Override + public CompletableFuture delete(String sessionId) { + return CompletableFuture.runAsync(() -> { + redis.del("session:" + sessionId); + }); + } +} +``` + +Then use it like any other store: + +```java +RedisSessionStore store = + new RedisSessionStore<>(redisClient, objectMapper); + +Session session = genkit.createSession( + SessionOptions.builder() + .store(store) + .initialState(new ConversationState("John")) + .build()); +``` + +### SessionData structure + +The `SessionData` object is what gets persisted. It contains: + +| Field | Type | Description | +|-------|------|-------------| +| `id` | `String` | Session ID | +| `state` | `S` | Your typed session state | +| `threads` | `Map>` | All conversation threads and their message histories | + +```java +// Access thread data directly +SessionData data = store.get("session-123").get(); +List mainThread = data.getThread("main"); +Map> allThreads = data.getThreads(); +``` + +## Sample + +See the [chat-session sample](https://github.com/xavidop/genkit-java/tree/main/samples/chat-session) for a complete multi-turn chat implementation with state, tools, and session persistence. diff --git a/docs/src/content/docs/dev-ui.md b/docs/src/content/docs/dev-ui.md new file mode 100644 index 000000000..44610ba07 --- /dev/null +++ b/docs/src/content/docs/dev-ui.md @@ -0,0 +1,55 @@ +--- +title: Dev UI & CLI +description: Local developer tools for testing and debugging AI workflows. +--- + +Genkit provides a developer UI and CLI for testing, debugging, and inspecting your AI logic. + +## Installing the CLI + +```bash +npm install -g genkit +``` + +## Starting the Dev UI + +Run your application with the Genkit CLI to launch the developer UI: + +```bash +genkit start -- ./run.sh +# Or: +genkit start -- mvn exec:java +``` + +The Dev UI launches at `http://localhost:4000` by default. + +## What the Dev UI provides + +- **List all registered actions** — Flows, models, tools, prompts, retrievers, evaluators +- **Run actions with test inputs** — Execute flows interactively +- **View traces** — Inspect execution logs with full span details +- **Manage datasets** — Create and manage evaluation datasets +- **Run evaluations** — Execute evaluators and review results + +## Reflection server + +When running in dev mode (`devMode(true)`), Genkit starts a reflection server on port 3100 (configurable via `reflectionPort()`). The Dev UI connects to this server. + +```java +Genkit genkit = Genkit.builder() + .options(GenkitOptions.builder() + .devMode(true) + .reflectionPort(3100) // Default port + .build()) + .build(); +``` + +## Running flows from the CLI + +```bash +# Run a flow +genkit flow:run tellJoke '"pirates"' + +# Run with streaming output +genkit flow:run tellJoke '"pirates"' -s +``` diff --git a/docs/src/content/docs/dotprompt.md b/docs/src/content/docs/dotprompt.md new file mode 100644 index 000000000..a25638b6a --- /dev/null +++ b/docs/src/content/docs/dotprompt.md @@ -0,0 +1,64 @@ +--- +title: DotPrompt +description: Manage prompts as template files with Handlebars support. +--- + +DotPrompt lets you load and use `.prompt` files with Handlebars templating for structured, reusable prompt management. + +## Loading a prompt + +Place `.prompt` files in `resources/prompts/`: + +``` +resources/ + prompts/ + recipe.prompt + recipe.robot.prompt # variant +``` + +```java +// Load a prompt from resources/prompts/recipe.prompt +ExecutablePrompt recipePrompt = genkit.prompt("recipe", RecipeInput.class); + +// Execute with typed input +ModelResponse response = recipePrompt.generate(new RecipeInput("pasta carbonara")); +``` + +## Prompt variants + +Variants let you create alternate versions of a prompt (e.g., different tones or personalities): + +```java +// Load the "robot" variant: recipe.robot.prompt +ExecutablePrompt robotPrompt = genkit.prompt( + "recipe", RecipeInput.class, "robot" +); +``` + +## Structured output from prompts + +Prompts work with structured output generation: + +```java +ExecutablePrompt prompt = genkit.prompt("italian-dish", DishRequest.class); +MenuItem dish = prompt.generate(new DishRequest("Italian"), MenuItem.class); +``` + +## Prompt file format + +Prompt files use Handlebars templates with YAML frontmatter: + +```handlebars +--- +model: openai/gpt-4o-mini +config: + temperature: 0.9 + maxOutputTokens: 500 +input: + schema: + ingredient: string + style?: string +--- +Create a {{style}} recipe using {{ingredient}} as the main ingredient. +Include step-by-step instructions. +``` diff --git a/docs/src/content/docs/evaluations.md b/docs/src/content/docs/evaluations.md new file mode 100644 index 000000000..ce19a171b --- /dev/null +++ b/docs/src/content/docs/evaluations.md @@ -0,0 +1,259 @@ +--- +title: Evaluations +description: Assess AI output quality with custom and pre-built evaluators. +--- + +Genkit provides a framework for evaluating AI output quality. You can define custom evaluators for your specific needs, use pre-built RAGAS-style metrics, manage evaluation datasets, and run evaluations against flows or models. + +## Custom evaluators + +Define evaluators to assess specific quality dimensions. Each evaluator receives an `EvalDataPoint` containing the input, output, context, and reference, and returns a `Score`. + +```java +import com.google.genkit.ai.evaluation.*; + +Evaluator lengthEvaluator = genkit.defineEvaluator( + "custom/length", + "Output Length", + "Evaluates whether the output has an appropriate length", + (dataPoint, options) -> { + String output = dataPoint.getOutput() != null + ? dataPoint.getOutput().toString() : ""; + int length = output.length(); + double score = (length >= 50 && length <= 500) + ? 1.0 : Math.min(length / 50.0, 1.0); + EvalStatus status = score >= 1.0 + ? EvalStatus.PASS : EvalStatus.FAIL; + + return EvalResponse.builder() + .testCaseId(dataPoint.getTestCaseId()) + .evaluation(Score.builder() + .score(score) + .status(status) + .details(ScoreDetails.builder() + .reasoning("Output length: " + length + " characters") + .build()) + .build()) + .build(); + }); +``` + +### Evaluator with options + +You can define evaluators that accept typed options: + +```java +Evaluator thresholdEvaluator = genkit.defineEvaluator( + "custom/threshold", + "Threshold Check", + "Checks output against a configurable threshold", + false, // isBilled + ThresholdOptions.class, + (dataPoint, options) -> { + double threshold = options.getThreshold(); + // ... evaluate against threshold + return EvalResponse.builder() + .testCaseId(dataPoint.getTestCaseId()) + .evaluation(Score.builder().score(score).build()) + .build(); + }); +``` + +### EvalDataPoint fields + +Each data point passed to your evaluator contains: + +| Field | Type | Description | +|-------|------|-------------| +| `testCaseId` | `String` | Unique identifier for the test case | +| `input` | `Object` | The input sent to the flow/model | +| `output` | `Object` | The output produced by the flow/model | +| `context` | `List` | Retrieved context documents (for RAG) | +| `reference` | `Object` | Expected/reference output | +| `custom` | `Map` | Custom metadata | +| `traceIds` | `List` | Associated trace IDs | + +### Score values + +Scores can be numeric, boolean, or string: + +```java +Score.builder().score(0.95).status(EvalStatus.PASS).build() // numeric +Score.builder().score(true).status(EvalStatus.PASS).build() // boolean +Score.builder().score("good").status(EvalStatus.PASS).build() // categorical +``` + +The `EvalStatus` enum has three values: `PASS`, `FAIL`, and `UNKNOWN`. + +## Managing datasets + +Genkit includes a `DatasetStore` for managing evaluation datasets. The default implementation (`LocalFileDatasetStore`) persists datasets to local files. + +### Creating a dataset + +```java +DatasetStore datasetStore = genkit.getDatasetStore(); + +List samples = List.of( + DatasetSample.builder() + .testCaseId("food_1") + .input("pizza") + .reference("A delicious Italian dish with a crispy crust and savory toppings") + .build(), + DatasetSample.builder() + .testCaseId("food_2") + .input("sushi") + .reference("Fresh, delicate rolls of rice and fish from Japan") + .build()); + +DatasetMetadata metadata = datasetStore.createDataset( + CreateDatasetRequest.builder() + .datasetId("food_descriptions") + .data(samples) + .datasetType(DatasetType.FLOW) + .targetAction("/flow/describeFood") + .metricRefs(List.of("custom/length", "custom/keywords")) + .build()); +``` + +### Listing and retrieving datasets + +```java +List datasets = datasetStore.listDatasets(); + +DatasetMetadata dataset = datasetStore.getDataset("food_descriptions"); +``` + +### Updating a dataset + +```java +datasetStore.updateDataset(UpdateDatasetRequest.builder() + .datasetId("food_descriptions") + .data(updatedSamples) + .version(dataset.getVersion()) // optimistic concurrency + .build()); +``` + +### Dataset types + +| Type | Description | +|------|-------------| +| `DatasetType.FLOW` | Dataset targets a flow | +| `DatasetType.MODEL` | Dataset targets a model | +| `DatasetType.CUSTOM` | Custom dataset type | + +## Running evaluations + +Run evaluations against a flow or model using a dataset: + +```java +EvalRunKey result = genkit.evaluate( + RunEvaluationRequest.builder() + .dataSource(DataSource.builder() + .datasetId("food_descriptions") + .build()) + .targetAction("/flow/describeFood") + .evaluators(List.of("custom/length", "custom/keywords")) + .build()); +``` + +### Inline data (no dataset) + +You can also provide data inline without creating a dataset first: + +```java +EvalRunKey result = genkit.evaluate( + RunEvaluationRequest.builder() + .dataSource(DataSource.builder() + .data(List.of( + DatasetSample.builder() + .testCaseId("test_1") + .input("What is Genkit?") + .reference("Genkit is an AI framework") + .build())) + .build()) + .targetAction("/flow/myFlow") + .evaluators(List.of("custom/length")) + .build()); +``` + +## Viewing evaluation results + +Evaluation results are stored in an `EvalStore`. The default implementation (`LocalFileEvalStore`) persists to local files. + +```java +EvalStore evalStore = genkit.getEvalStore(); + +// List all evaluation runs +List runs = evalStore.list(); + +// Load a specific run +EvalRun run = evalStore.load(result); // result from evaluate() + +for (EvalResult evalResult : run.getResults()) { + System.out.println("Test: " + evalResult.getTestCaseId()); + for (EvalMetric metric : evalResult.getMetrics()) { + System.out.println(" " + metric.getEvaluator() + + ": " + metric.getScore() + + " (" + metric.getStatus() + ")"); + } +} +``` + +## Pre-built evaluators plugin + +Use the evaluators plugin for RAGAS-style metrics without writing custom evaluators: + +```java +import com.google.genkit.plugins.evaluators.EvaluatorsPlugin; +import com.google.genkit.plugins.evaluators.EvaluatorsPluginOptions; +import com.google.genkit.plugins.evaluators.GenkitMetric; + +Genkit genkit = Genkit.builder() + .plugin(OpenAIPlugin.create()) + .plugin(EvaluatorsPlugin.create( + EvaluatorsPluginOptions.builder() + .judge("openai/gpt-4o-mini") + .metrics(List.of( + GenkitMetric.FAITHFULNESS, + GenkitMetric.ANSWER_RELEVANCY, + GenkitMetric.ANSWER_ACCURACY, + GenkitMetric.MALICIOUSNESS, + GenkitMetric.REGEX, + GenkitMetric.DEEP_EQUAL, + GenkitMetric.JSONATA + )) + .build())) + .build(); +``` + +Or enable all metrics at once: + +```java +EvaluatorsPluginOptions.builder() + .useAllMetrics() + .judge("openai/gpt-4o-mini") + .embedder("openai/text-embedding-3-small") + .build() +``` + +### Available metrics + +| Metric | Type | Description | +|--------|------|-------------| +| `FAITHFULNESS` | LLM-based | Factual accuracy against provided context | +| `ANSWER_RELEVANCY` | LLM-based | Answer pertains to the question | +| `ANSWER_ACCURACY` | LLM-based | Matches reference answer | +| `MALICIOUSNESS` | LLM-based | Detects harmful content | +| `REGEX` | Programmatic | Pattern matching against output | +| `DEEP_EQUAL` | Programmatic | JSON deep equality comparison | +| `JSONATA` | Programmatic | JSONata expression evaluation | + +LLM-based metrics require a `judge` model. Programmatic metrics run locally without any API calls. + +See the [evaluators plugin docs](/genkit-java/plugins/evaluators/) for full details. + +## Samples + +- [evaluations sample](https://github.com/xavidop/genkit-java/tree/main/samples/evaluations) — Custom evaluators, dataset management, and running evaluations +- [evaluators-plugin sample](https://github.com/xavidop/genkit-java/tree/main/samples/evaluators-plugin) — Pre-built RAGAS metrics diff --git a/docs/src/content/docs/flows.mdx b/docs/src/content/docs/flows.mdx new file mode 100644 index 000000000..c9a77d5f4 --- /dev/null +++ b/docs/src/content/docs/flows.mdx @@ -0,0 +1,72 @@ +--- +title: Creating Flows +description: Define observable, traceable AI workflows that can be exposed as HTTP endpoints. +--- + +Flows are observable, traceable AI workflows. They wrap your AI logic to provide type safety, debugging support, and easy deployment as HTTP endpoints. + +## Defining a flow + +```java +// Simple flow with typed input/output +Flow greetFlow = genkit.defineFlow( + "greeting", + String.class, + String.class, + name -> "Hello, " + name + "!"); +``` + +## AI-powered flows + +```java +// Flow with AI generation and context access +Flow jokeFlow = genkit.defineFlow( + "tellJoke", + String.class, + String.class, + (ctx, topic) -> { + ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("Tell me a short, funny joke about: " + topic) + .build()); + return response.getText(); + }); +``` + +## Running flows + +```java +// Run a flow programmatically +String result = genkit.runFlow("greeting", "World"); +// Returns: "Hello, World!" +``` + +## Flows as HTTP endpoints + +When using the Jetty or Spring Boot plugins, all defined flows are automatically exposed as HTTP endpoints: + +```bash +# Call a flow via HTTP +curl -X POST http://localhost:8080/api/flows/tellJoke \ + -H "Content-Type: application/json" \ + -d '{"data": "pirates"}' +``` + +## Why use flows? + +- **Type-safe inputs and outputs** — Define clear schemas for your data +- **Integrates with the Dev UI** — Test and debug flows visually +- **Easy deployment** — Automatically exposed as HTTP endpoints +- **Built-in tracing** — Every flow execution is traced with OpenTelemetry +- **Composable** — Flows can call other flows + +## Next steps + +import { LinkCard, CardGrid } from '@astrojs/starlight/components'; + + + + + + diff --git a/docs/src/content/docs/getting-started.mdx b/docs/src/content/docs/getting-started.mdx new file mode 100644 index 000000000..cb1a87213 --- /dev/null +++ b/docs/src/content/docs/getting-started.mdx @@ -0,0 +1,153 @@ +--- +title: Get Started +description: Set up your first Genkit Java project and start building AI features. +--- + +This guide shows you how to set up a Genkit Java project and make your first AI generation call. + +## Prerequisites + +- **Java 21+** +- **Maven 3.6+** +- An API key from your preferred model provider (e.g., OpenAI, Google AI) + +## Installation + +Add the following dependencies to your Maven `pom.xml`: + +```xml + + + com.google.genkit + genkit + 1.0.0-SNAPSHOT + + + + + com.google.genkit + genkit-plugin-openai + 1.0.0-SNAPSHOT + + + + + com.google.genkit + genkit-plugin-jetty + 1.0.0-SNAPSHOT + +``` + +## Configure your API key + +Set the environment variable for your model provider: + +```bash +# For OpenAI +export OPENAI_API_KEY=your-api-key + +# For Google GenAI (Gemini) +export GOOGLE_GENAI_API_KEY=your-api-key + +# For Anthropic (Claude) +export ANTHROPIC_API_KEY=your-api-key +``` + +## Create your first application + +```java +import com.google.genkit.Genkit; +import com.google.genkit.GenkitOptions; +import com.google.genkit.ai.GenerateOptions; +import com.google.genkit.ai.GenerationConfig; +import com.google.genkit.ai.ModelResponse; +import com.google.genkit.plugins.openai.OpenAIPlugin; +import com.google.genkit.plugins.jetty.JettyPlugin; +import com.google.genkit.plugins.jetty.JettyPluginOptions; + +public class Main { + public static void main(String[] args) throws Exception { + // Create server plugin + JettyPlugin jetty = new JettyPlugin(JettyPluginOptions.builder() + .port(8080) + .build()); + + // Create Genkit with plugins + Genkit genkit = Genkit.builder() + .options(GenkitOptions.builder() + .devMode(true) + .reflectionPort(3100) + .build()) + .plugin(OpenAIPlugin.create()) + .plugin(jetty) + .build(); + + // Generate text + ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("Tell me a fun fact!") + .config(GenerationConfig.builder() + .temperature(0.9) + .maxOutputTokens(200) + .build()) + .build()); + + System.out.println(response.getText()); + + // Start the HTTP server (blocks until stopped) + jetty.start(); + } +} +``` + +## Running with the Dev UI + +Install the Genkit CLI and start with the developer UI: + +```bash +# Install Genkit CLI +npm install -g genkit + +# Start your app with Dev UI +genkit start -- mvn exec:java +``` + +The Dev UI launches at `http://localhost:4000` where you can test flows, inspect traces, and debug your AI logic. + +## Using Google GenAI (Gemini) instead + +Swap the plugin to use Gemini models: + +```xml + + com.google.genkit + genkit-plugin-google-genai + 1.0.0-SNAPSHOT + +``` + +```java +import com.google.genkit.plugins.googlegenai.GoogleGenAIPlugin; + +Genkit genkit = Genkit.builder() + .plugin(GoogleGenAIPlugin.create(System.getenv("GOOGLE_GENAI_API_KEY"))) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("googleai/gemini-2.0-flash") + .prompt("Tell me a fun fact!") + .build()); +``` + +## Next steps + +import { LinkCard, CardGrid } from '@astrojs/starlight/components'; + + + + + + + diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx new file mode 100644 index 000000000..97656432f --- /dev/null +++ b/docs/src/content/docs/index.mdx @@ -0,0 +1,260 @@ +--- +title: Genkit for Java (Unofficial) +description: The AI Framework for Java Applications. Build, test, and deploy AI-powered features with a unified API. +template: splash +hero: + title: | + The AI Framework for + Java Applications + tagline: Build AI-powered applications in Java with a unified API for any model provider. Supports OpenAI, Gemini, Claude, Bedrock, and 15+ more providers out of the box. + actions: + - text: Get Started + link: /genkit-java/getting-started/ + icon: right-arrow + variant: primary + - text: View on GitHub + link: https://github.com/genkit-ai/genkit-java + icon: external + variant: minimal +--- + +import { Card, CardGrid, LinkCard, Tabs, TabItem } from '@astrojs/starlight/components'; + +## Get started with just a few lines of code + + + + ```java + import com.google.genkit.Genkit; + import com.google.genkit.plugins.googlegenai.GoogleGenAIPlugin; + + Genkit genkit = Genkit.builder() + .plugin(GoogleGenAIPlugin.create()) + .build(); + + String text = genkit.generate(GenerateOptions.builder() + .model("googleai/gemini-3-flash") + .prompt("Why is Genkit awesome?") + .build()).getText(); + ``` + + + ```java + import com.google.genkit.Genkit; + import com.google.genkit.plugins.openai.OpenAIPlugin; + + Genkit genkit = Genkit.builder() + .plugin(OpenAIPlugin.create()) + .build(); + + String text = genkit.generate(GenerateOptions.builder() + .model("openai/gpt-5.2") + .prompt("Why is Genkit awesome?") + .build()).getText(); + ``` + + + ```java + import com.google.genkit.Genkit; + import com.google.genkit.plugins.anthropic.AnthropicPlugin; + + Genkit genkit = Genkit.builder() + .plugin(AnthropicPlugin.create()) + .build(); + + String text = genkit.generate(GenerateOptions.builder() + .model("anthropic/claude-sonnet-4.6-20260301") + .prompt("Why is Genkit awesome?") + .build()).getText(); + ``` + + + ```java + import com.google.genkit.Genkit; + import com.google.genkit.plugins.xai.XAIPlugin; + + Genkit genkit = Genkit.builder() + .plugin(XAIPlugin.create()) + .build(); + + String text = genkit.generate(GenerateOptions.builder() + .model("xai/grok-4-mini") + .prompt("Why is Genkit awesome?") + .build()).getText(); + ``` + + + ```java + import com.google.genkit.Genkit; + import com.google.genkit.plugins.deepseek.DeepSeekPlugin; + + Genkit genkit = Genkit.builder() + .plugin(DeepSeekPlugin.create()) + .build(); + + String text = genkit.generate(GenerateOptions.builder() + .model("deepseek/deepseek-v3") + .prompt("Why is Genkit awesome?") + .build()).getText(); + ``` + + + ```java + import com.google.genkit.Genkit; + import com.google.genkit.plugins.ollama.OllamaPlugin; + + Genkit genkit = Genkit.builder() + .plugin(OllamaPlugin.create()) + .build(); + + String text = genkit.generate(GenerateOptions.builder() + .model("ollama/gemma3n") + .prompt("Why is Genkit awesome?") + .build()).getText(); + ``` + + + +## Simplified AI Integration + +The fastest way to build AI features into your Java apps. + + + + Use Google AI, OpenAI, Claude, Bedrock, and Ollama through one SDK. Switch models with a single line change. + + + Structure chat, RAG, tool use, and agents with built-in primitives. Define type-safe flows with input/output schemas. + + + Local dev UI, debugging, tracing with OpenTelemetry, and deployment to Spring Boot, Jetty, or Firebase Cloud Functions. + + + Model providers, vector databases, MCP, evaluators, and server frameworks — all as simple plugin integrations. + + + +## Deploy anywhere + + + + ```java + import com.google.genkit.plugins.jetty.JettyPlugin; + + JettyPlugin jetty = new JettyPlugin(JettyPluginOptions.builder() + .port(8080).build()); + + Genkit genkit = Genkit.builder() + .plugin(GoogleGenAIPlugin.create()) + .plugin(jetty) + .build(); + + genkit.defineFlow("tellJoke", String.class, String.class, + (ctx, topic) -> genkit.generate(GenerateOptions.builder() + .model("googleai/gemini-3-flash") + .prompt("Tell a joke about: " + topic) + .build()).getText()); + + jetty.start(); + ``` + + + ```java + import com.google.genkit.plugins.spring.SpringPlugin; + + @SpringBootApplication + public class App { + public static void main(String[] args) { + SpringPlugin spring = new SpringPlugin(); + + Genkit genkit = Genkit.builder() + .plugin(GoogleGenAIPlugin.create()) + .plugin(spring) + .build(); + + genkit.defineFlow("tellJoke", String.class, String.class, + (ctx, topic) -> genkit.generate(GenerateOptions.builder() + .model("googleai/gemini-3-flash") + .prompt("Tell a joke about: " + topic) + .build()).getText()); + + spring.start(args); + } + } + ``` + + + ```java + import com.google.genkit.plugins.firebase.functions.OnCallGenkit; + + public class MyFunction implements HttpFunction { + private final OnCallGenkit fn; + + public MyFunction() { + Genkit genkit = Genkit.builder() + .plugin(GoogleGenAIPlugin.create()) + .plugin(FirebasePlugin.builder().build()) + .build(); + + genkit.defineFlow("tellJoke", String.class, String.class, + (ctx, topic) -> genkit.generate(GenerateOptions.builder() + .model("googleai/gemini-3-flash") + .prompt("Tell a joke about: " + topic) + .build()).getText()); + + this.fn = OnCallGenkit.fromFlow(genkit, "tellJoke"); + } + + @Override + public void service(HttpRequest req, HttpResponse res) throws IOException { + fn.service(req, res); + } + } + ``` + + + +## Supported Providers + + + + + + + + + + + + + + + + +## Vector Databases + + + + + + + + + +## Deployment & Integrations + + + + + + + + +--- + + + + + + + diff --git a/docs/src/content/docs/interrupts.md b/docs/src/content/docs/interrupts.md new file mode 100644 index 000000000..0f02bd85c --- /dev/null +++ b/docs/src/content/docs/interrupts.md @@ -0,0 +1,192 @@ +--- +title: Interrupts +description: Pause AI generation for human-in-the-loop interactions. +--- + +Interrupts allow you to pause execution when an AI model calls a tool, request input from a human or external system, and then resume processing with the response. This enables human-in-the-loop patterns like approval workflows, confirmation dialogs, and data collection. + +## Defining an interrupt tool + +Use `genkit.defineInterrupt()` to create a tool that always pauses execution when called by the model. You specify the input/output types and an optional metadata extractor: + +```java +import com.google.genkit.ai.InterruptConfig; + +Tool confirmTransferTool = + genkit.defineInterrupt( + InterruptConfig.builder() + .name("confirmTransfer") + .description("Request user confirmation before executing a money transfer.") + .inputType(TransferRequest.class) + .outputType(ConfirmationOutput.class) + .inputSchema(Map.of( + "type", "object", + "properties", Map.of( + "recipient", Map.of("type", "string"), + "amount", Map.of("type", "number"), + "reason", Map.of("type", "string")), + "required", new String[]{"recipient", "amount"})) + .requestMetadata(input -> Map.of( + "type", "transfer_confirmation", + "recipient", input.getRecipient(), + "amount", input.getAmount())) + .build()); +``` + +When the model decides to call this tool, a `ToolInterruptException` is thrown internally — you don't need to handle this yourself. Genkit captures the interrupt and makes it available for you to respond to. + +## Using interrupts with Chat + +The most common way to use interrupts is through `Chat` sessions: + +### 1. Send a message and check for interrupts + +```java +Chat chat = session.chat( + ChatOptions.builder() + .model("openai/gpt-4o-mini") + .system("You are a banking assistant. Use confirmTransfer for transfers.") + .tools(List.of(confirmTransferTool)) + .build()); + +ModelResponse response = chat.send("Transfer $250 to John for dinner"); + +if (chat.hasPendingInterrupts()) { + List interrupts = chat.getPendingInterrupts(); + // Present to user for confirmation... +} +``` + +### 2. Inspect the interrupt + +Each `InterruptRequest` contains the original `ToolRequest` (what the model wanted to do) and any metadata you configured: + +```java +InterruptRequest interrupt = interrupts.get(0); +ToolRequest toolRequest = interrupt.getToolRequest(); +Map metadata = interrupt.getMetadata(); + +System.out.println("Tool: " + toolRequest.getName()); +System.out.println("Recipient: " + metadata.get("recipient")); +System.out.println("Amount: " + metadata.get("amount")); +``` + +### 3. Resume with a response + +After getting user input, create a response and resume the conversation: + +```java +// User approved the transfer +ConfirmationOutput userResponse = new ConfirmationOutput(true, "User approved"); +ToolResponse toolResponse = interrupt.respond(userResponse); + +ModelResponse resumed = chat.send( + "User confirmed the transfer.", + Chat.SendOptions.builder() + .resumeOptions(ResumeOptions.builder() + .respond(List.of(toolResponse)) + .build()) + .build()); +``` + +### 4. Or restart with different input + +If the user wants to modify the request, you can restart the tool call with updated input: + +```java +ToolRequest restartRequest = interrupt.restart( + Map.of("modified", true), // updated metadata + new TransferRequest("John", 200) // new input (changed amount) +); + +ModelResponse resumed = chat.send( + "User changed the amount to $200.", + Chat.SendOptions.builder() + .resumeOptions(ResumeOptions.builder() + .restart(List.of(restartRequest)) + .build()) + .build()); +``` + +## Using interrupts with generate() + +You can also use interrupts directly with `genkit.generate()` without a Chat session: + +```java +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("Transfer $150 to Alice") + .tools(List.of(confirmTransferTool)) + .build()); + +if (response.isInterrupted()) { + Part interruptPart = response.getInterrupts().get(0); + + // Respond using the tool helper + Part responsePart = confirmTransferTool.respond( + interruptPart, + new ConfirmationOutput(true, "Approved"), + Map.of()); + + // Resume generation with the previous messages + ModelResponse resumed = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .messages(response.getMessages()) + .tools(List.of(confirmTransferTool)) + .resume(ResumeOptions.builder() + .respond(responsePart.getToolResponse()) + .build()) + .build()); +} +``` + +## Multiple interrupts + +A single model turn can trigger multiple interrupt tools. Handle them all before resuming: + +```java +if (chat.hasPendingInterrupts()) { + List interrupts = chat.getPendingInterrupts(); + List responses = new ArrayList<>(); + + for (InterruptRequest interrupt : interrupts) { + // Present each to user and collect responses + ToolResponse response = interrupt.respond(getUserInput(interrupt)); + responses.add(response); + } + + ModelResponse resumed = chat.send("User responded to all prompts.", + Chat.SendOptions.builder() + .resumeOptions(ResumeOptions.builder() + .respond(responses) + .build()) + .build()); +} +``` + +## InterruptConfig options + +| Option | Description | +|--------|-------------| +| `name` | Tool name the model will call | +| `description` | Description to help the model decide when to use the tool | +| `inputType` | Java class for the tool's input | +| `outputType` | Java class for the expected response | +| `inputSchema` | JSON Schema for the input (helps the model) | +| `outputSchema` | JSON Schema for the output | +| `requestMetadata` | Function to extract metadata from the input for display | + +## Flow + +1. You define interrupt tools and provide them to the model +2. The model calls an interrupt tool → execution pauses +3. You inspect `InterruptRequest` (what the model wanted to do) +4. You collect user input and create either a `respond()` or `restart()` +5. You resume the conversation with `ResumeOptions` +6. The model continues with the tool's response + +## Sample + +See the [interrupts sample](https://github.com/xavidop/genkit-java/tree/main/samples/interrupts) for a complete banking transfer confirmation example. diff --git a/docs/src/content/docs/middleware.md b/docs/src/content/docs/middleware.md new file mode 100644 index 000000000..a125e43e4 --- /dev/null +++ b/docs/src/content/docs/middleware.md @@ -0,0 +1,230 @@ +--- +title: Middleware +description: Add cross-cutting concerns to your AI workflows with middleware. +--- + +Middleware lets you intercept and modify the behavior of flow executions. Use middleware for logging, caching, rate limiting, retries, input validation, and more. Middleware follows the chain-of-responsibility pattern — each middleware can modify the request, call the next handler, and modify the response. + +## Defining middleware + +A middleware is a function that receives the request, an `ActionContext`, and a `next` function to call the next handler in the chain: + +```java +import com.google.genkit.core.middleware.Middleware; + +Middleware loggingMiddleware = (request, context, next) -> { + System.out.println("Request: " + request); + String result = next.apply(request, context); + System.out.println("Response: " + result); + return result; +}; +``` + +## Attaching middleware to flows + +Pass middleware as a list when defining a flow: + +```java +List> middleware = List.of( + loggingMiddleware, + validationMiddleware, + retryMiddleware +); + +Flow chatFlow = genkit.defineFlow( + "chat", String.class, String.class, + (ctx, userMessage) -> { + ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt(userMessage) + .build()); + return response.getText(); + }, + middleware); +``` + +Middleware executes in order — the first middleware in the list runs first (outermost), wrapping all subsequent middleware and the flow handler. + +## Built-in middleware + +The `CommonMiddleware` class provides factory methods for common patterns: + +### Logging + +```java +import com.google.genkit.core.middleware.CommonMiddleware; + +// Default logger +Middleware logging = CommonMiddleware.logging("chat"); + +// Custom logger +Middleware logging = CommonMiddleware.logging("chat", myLogger); +``` + +### Retry with exponential backoff + +```java +// Retry up to 3 times with 100ms initial delay +Middleware retry = CommonMiddleware.retry(3, 100); + +// With custom retry predicate +Middleware retry = CommonMiddleware.retry(3, 100, + error -> error.getMessage().contains("rate limit")); +``` + +### Input validation + +```java +Middleware validate = CommonMiddleware.validate(input -> { + if (input == null || input.trim().isEmpty()) { + throw new GenkitException("Input cannot be empty"); + } + if (input.length() > 1000) { + throw new GenkitException("Input exceeds maximum length"); + } +}); +``` + +### Request and response transformation + +```java +// Sanitize input +Middleware sanitize = CommonMiddleware.transformRequest( + input -> input.trim().replaceAll("\\s+", " ")); + +// Format output +Middleware format = CommonMiddleware.transformResponse( + output -> "[" + Instant.now() + "] " + output); +``` + +### Caching + +```java +import com.google.genkit.core.middleware.MiddlewareCache; + +Middleware cache = CommonMiddleware.cache( + myCache, // MiddlewareCache implementation + input -> input.hashCode() + "" // key extractor +); +``` + +The `MiddlewareCache` interface requires `get(String key)` and `put(String key, O value)` methods. + +### Rate limiting + +```java +// Max 10 requests per 60 seconds +Middleware rateLimit = CommonMiddleware.rateLimit(10, 60_000); +``` + +### Timeout + +```java +// 30 second timeout +Middleware timeout = CommonMiddleware.timeout(30_000); +``` + +### Error handling + +```java +Middleware errorHandler = CommonMiddleware.errorHandler( + error -> "Sorry, something went wrong: " + error.getMessage()); +``` + +### Conditional middleware + +Apply middleware only when a condition is met: + +```java +Middleware conditional = CommonMiddleware.conditional( + (request, context) -> request.length() > 100, // only for long inputs + CommonMiddleware.logging("long-input") +); +``` + +### Before/after hooks + +```java +Middleware hooks = CommonMiddleware.beforeAfter( + (request, context) -> System.out.println("Before: " + request), + (response, context) -> System.out.println("After: " + response) +); +``` + +### Timing + +```java +Middleware timing = CommonMiddleware.timing( + duration -> System.out.println("Took " + duration + "ms")); +``` + +## Building a middleware chain + +Use `MiddlewareChain` for more control over middleware ordering: + +```java +import com.google.genkit.core.middleware.MiddlewareChain; + +MiddlewareChain chain = MiddlewareChain.of( + CommonMiddleware.logging("chat"), + CommonMiddleware.validate(input -> { /* ... */ }), + CommonMiddleware.retry(3, 100)); + +// Add middleware dynamically +chain.use(CommonMiddleware.timing(d -> log.info("{}ms", d))); +chain.useFirst(CommonMiddleware.rateLimit(10, 60_000)); // insert at beginning + +// Execute manually +String result = chain.execute(input, context, (ctx, req) -> { + // final handler + return genkit.generate(...).getText(); +}); +``` + +## Custom middleware example + +A metrics-collecting middleware: + +```java +Map requestCounts = new ConcurrentHashMap<>(); +Map> responseTimes = new ConcurrentHashMap<>(); + +Middleware metricsMiddleware = (request, context, next) -> { + requestCounts.computeIfAbsent("chat", k -> new AtomicLong(0)) + .incrementAndGet(); + long start = System.currentTimeMillis(); + try { + String result = next.apply(request, context); + long duration = System.currentTimeMillis() - start; + responseTimes.computeIfAbsent("chat", k -> new ArrayList<>()) + .add(duration); + return result; + } catch (GenkitException e) { + // Track errors too + throw e; + } +}; +``` + +## Built-in middleware reference + +| Factory Method | Description | +|---------------|-------------| +| `logging(name)` | Log requests and responses | +| `retry(maxRetries, delayMs)` | Retry with exponential backoff | +| `validate(validator)` | Validate input before processing | +| `transformRequest(fn)` | Transform input before processing | +| `transformResponse(fn)` | Transform output after processing | +| `cache(cache, keyExtractor)` | Cache responses | +| `rateLimit(maxReqs, windowMs)` | Limit request rate | +| `timeout(timeoutMs)` | Fail if execution exceeds timeout | +| `errorHandler(handler)` | Return fallback on error | +| `conditional(predicate, mw)` | Apply middleware conditionally | +| `beforeAfter(before, after)` | Run hooks before and after | +| `timing(callback)` | Measure execution duration | + +## Samples + +- [middleware sample](https://github.com/xavidop/genkit-java/tree/main/samples/middleware) — Custom and built-in middleware patterns +- [middleware-v2 sample](https://github.com/xavidop/genkit-java/tree/main/samples/middleware-v2) — Updated middleware approaches diff --git a/docs/src/content/docs/models.mdx b/docs/src/content/docs/models.mdx new file mode 100644 index 000000000..0c9fd1c9f --- /dev/null +++ b/docs/src/content/docs/models.mdx @@ -0,0 +1,117 @@ +--- +title: Generating Content +description: Use Genkit's unified generation API to work with any AI model. +--- + +Genkit provides a unified interface for working with generative AI models from any supported provider. Configure a model plugin once, then call any model through the same API. + +## The generate() method + +The primary interface for interacting with AI models is `generate()`: + +```java +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("Invent a menu item for a pirate-themed restaurant.") + .build()); + +System.out.println(response.getText()); +``` + +## Model parameters + +Control how the model generates content with configuration options: + +```java +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("Tell me a creative story.") + .config(GenerationConfig.builder() + .temperature(0.9) // 0.0-2.0, higher = more creative + .maxOutputTokens(512) // Limit output length + .topP(0.95) // Nucleus sampling + .topK(40) // Top-K sampling + .build()) + .build()); +``` + +### Parameter reference + +| Parameter | Description | +|-----------|-------------| +| `temperature` | Controls randomness. Low values (0.0-1.0) = more deterministic, high values (>1.0) = more creative | +| `maxOutputTokens` | Maximum number of tokens to generate | +| `topP` | Nucleus sampling — cumulative probability threshold (0.0-1.0) | +| `topK` | Limits token selection to the top K most likely tokens | +| `stopSequences` | Character sequences that signal the end of generation | + +## Switching models + +Changing models is as simple as changing the model string: + +```java +// Use OpenAI +genkit.generate(GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("Hello!").build()); + +// Use Gemini +genkit.generate(GenerateOptions.builder() + .model("googleai/gemini-2.0-flash") + .prompt("Hello!").build()); + +// Use Claude +genkit.generate(GenerateOptions.builder() + .model("anthropic/claude-sonnet-4-5-20250929") + .prompt("Hello!").build()); + +// Use a local Ollama model +genkit.generate(GenerateOptions.builder() + .model("ollama/gemma3n") + .prompt("Hello!").build()); +``` + +## Providing context documents + +Pass documents for context-aware generation (useful for RAG): + +```java +List docs = List.of( + Document.fromText("Paris is the capital of France."), + Document.fromText("Berlin is the capital of Germany.") +); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("What is the capital of France?") + .docs(docs) + .build()); +``` + +## Using tools + +Pass tools that models can call during generation: + +```java +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("What's the weather in Paris?") + .tools(List.of(weatherTool)) + .build()); +``` + +See [Tool Calling](/genkit-java/tools/) for complete documentation on defining and using tools. + +## Next steps + +import { LinkCard, CardGrid } from '@astrojs/starlight/components'; + + + + + + diff --git a/docs/src/content/docs/multi-agent.md b/docs/src/content/docs/multi-agent.md new file mode 100644 index 000000000..7b0228a34 --- /dev/null +++ b/docs/src/content/docs/multi-agent.md @@ -0,0 +1,155 @@ +--- +title: Multi-Agent +description: Build multi-agent AI orchestration patterns. +--- + +Multi-agent systems coordinate multiple AI agents, each with specialized capabilities, to solve complex tasks. In Genkit, agents are defined with their own system prompt, model, and tools. A triage agent routes user requests to the right specialist by calling that agent as a tool, triggering an automatic handoff. + +## Defining agents + +Use `genkit.defineAgent()` to create specialized agents. Each agent has its own system prompt, model, tools, and optional generation config: + +```java +import com.google.genkit.ai.Agent; +import com.google.genkit.ai.AgentConfig; + +// Define tools for the reservation agent +Tool makeReservationTool = genkit.defineTool( + "makeReservation", "Makes a restaurant reservation", + ReservationInput.class, String.class, + (ctx, input) -> "Reserved table for " + input.getPartySize() + + " at " + input.getTime()); + +Tool cancelReservationTool = genkit.defineTool( + "cancelReservation", "Cancels a reservation", + String.class, String.class, + (ctx, id) -> "Reservation " + id + " cancelled"); + +// Create a specialized agent +Agent reservationAgent = genkit.defineAgent( + AgentConfig.builder() + .name("reservationAgent") + .description("Handles restaurant reservations. " + + "Transfer here when the customer wants to book, " + + "modify, or cancel a reservation.") + .system("You are a reservation specialist. Help customers " + + "book and manage restaurant reservations.") + .model("openai/gpt-4o-mini") + .tools(List.of(makeReservationTool, cancelReservationTool)) + .config(GenerationConfig.builder().temperature(0.3).build()) + .build()); +``` + +## Creating the triage agent + +The triage (orchestrator) agent has sub-agents listed in its config. Genkit automatically registers each sub-agent as a tool that the triage agent can call: + +```java +Agent menuAgent = genkit.defineAgent( + AgentConfig.builder() + .name("menuAgent") + .description("Provides menu information and recommendations.") + .system("You are a menu expert. Help customers explore " + + "the menu and make recommendations.") + .model("openai/gpt-4o-mini") + .tools(List.of(getMenuTool, searchMenuTool)) + .build()); + +// Triage agent routes to specialists +Agent triageAgent = genkit.defineAgent( + AgentConfig.builder() + .name("triageAgent") + .description("Routes customer requests to the right specialist") + .system("You are the main customer service agent. Route requests:\n" + + "- reservationAgent: for booking/modifying/cancelling reservations\n" + + "- menuAgent: for menu questions and recommendations\n" + + "Transfer to the appropriate specialist using their tools.") + .model("openai/gpt-4o-mini") + .agents(List.of( + reservationAgent.getConfig(), + menuAgent.getConfig())) + .build()); +``` + +## Running a multi-agent chat + +Use `genkit.getAllToolsForAgent()` to get the triage agent's own tools plus all sub-agent tools, then start a chat session: + +```java +List> allTools = genkit.getAllToolsForAgent(triageAgent); + +Session session = genkit.createSession(); +Chat chat = session.chat( + ChatOptions.builder() + .model(triageAgent.getModel()) + .system(triageAgent.getSystem()) + .tools(allTools) + .build()); + +// User asks about the menu → routes to menuAgent +ModelResponse r1 = chat.send("What appetizers do you have?"); + +// User wants to book → routes to reservationAgent +ModelResponse r2 = chat.send("Great, book a table for 4 at 7pm"); +``` + +## How agent handoff works + +When the triage agent decides to delegate, it calls a sub-agent's tool. This triggers an `AgentHandoffException` internally, which Genkit handles automatically: + +1. The triage agent calls the sub-agent tool (e.g., `reservationAgent`) +2. Genkit catches the handoff and switches the chat's active system prompt, model, and tools to the target agent +3. Subsequent messages are handled by the new agent until another handoff occurs + +You can check which agent is currently active: + +```java +String currentAgent = chat.getCurrentAgentName(); +System.out.println("Now talking to: " + currentAgent); +``` + +## Stateful multi-agent conversations + +Combine agents with session state to track context across handoffs: + +```java +Session session = genkit.createSession( + SessionOptions.builder() + .store(sessionStore) + .initialState(new CustomerState("John")) + .build()); + +Chat chat = session.chat( + ChatOptions.builder() + .model(triageAgent.getModel()) + .system(triageAgent.getSystem()) + .tools(allTools) + .build()); + +// State persists across agent handoffs +CustomerState state = session.getState(); +``` + +## Agent registry + +All defined agents are registered in a global agent registry. You can look up agents by name: + +```java +Agent agent = genkit.getAgent("reservationAgent"); +Map allAgents = genkit.getAgentRegistry(); +``` + +## Patterns + +### Orchestrator pattern +A central triage agent delegates to specialized agents based on the user's intent. Best for customer service, help desks, and general-purpose assistants. + +### Pipeline pattern +Agents process data sequentially, each contributing to the result. Define each stage as an agent and chain them in your flow logic. + +### Debate pattern +Multiple agents propose different solutions, then a judge agent selects the best one. Useful for creative tasks or complex decision-making. + +## Sample + +See the [multi-agent sample](https://github.com/xavidop/genkit-java/tree/main/samples/multi-agent) for a complete restaurant assistant with triage, reservation, and menu agents. diff --git a/docs/src/content/docs/observability.md b/docs/src/content/docs/observability.md new file mode 100644 index 000000000..3d77c425c --- /dev/null +++ b/docs/src/content/docs/observability.md @@ -0,0 +1,86 @@ +--- +title: Observability +description: Comprehensive observability with OpenTelemetry tracing and metrics. +--- + +Genkit Java provides comprehensive observability through OpenTelemetry integration. All actions (models, tools, flows) are automatically instrumented. + +## Tracing + +All actions are automatically traced with rich metadata: + +| Attribute | Description | +|-----------|-------------| +| `genkit:name` | Action name (e.g., `openai/gpt-4o-mini`) | +| `genkit:type` | `action`, `flow`, `flowStep`, `util` | +| `genkit:metadata:subtype` | `model`, `tool`, `flow`, `embedder`, etc. | +| `genkit:path` | Full execution path | +| `genkit:input` | Full request data | +| `genkit:output` | Full response data | +| `genkit:sessionId` | Session identifier for multi-turn conversations | + +Example trace span: + +``` +genkit:name = "openai/gpt-4o-mini" +genkit:type = "action" +genkit:metadata:subtype = "model" +genkit:path = "/{myFlow,t:flow}/{openai/gpt-4o-mini,t:action,s:model}" +genkit:sessionId = "user-123" +``` + +## Metrics + +The SDK exposes OpenTelemetry metrics for monitoring: + +### Model generation metrics + +| Metric | Description | +|--------|-------------| +| `genkit/ai/generate/requests` | Request count | +| `genkit/ai/generate/latency` | Latency (ms) | +| `genkit/ai/generate/input/tokens` | Input token count | +| `genkit/ai/generate/output/tokens` | Output token count | +| `genkit/ai/generate/input/characters` | Input character count | +| `genkit/ai/generate/output/characters` | Output character count | +| `genkit/ai/generate/input/images` | Input image count | +| `genkit/ai/generate/output/images` | Output image count | +| `genkit/ai/generate/thinking/tokens` | Thinking/reasoning token count | + +### Tool and flow metrics + +| Metric | Description | +|--------|-------------| +| `genkit/tool/requests` | Tool execution request count | +| `genkit/tool/latency` | Tool execution latency (ms) | +| `genkit/feature/requests` | Flow request count | +| `genkit/feature/latency` | Flow latency (ms) | +| `genkit/action/requests` | General action request count | +| `genkit/action/latency` | General action latency (ms) | + +All metrics include attributes: `modelName`, `featureName`, `path`, `status` (success/failure), and `source` (java). + +## Usage tracking + +Model responses include detailed usage statistics: + +```java +ModelResponse response = genkit.generate(options); +Usage usage = response.getUsage(); + +System.out.println("Input tokens: " + usage.getInputTokens()); +System.out.println("Output tokens: " + usage.getOutputTokens()); +System.out.println("Latency: " + response.getLatencyMs() + "ms"); +``` + +## Firebase Telemetry + +For production deployments, the Firebase plugin can export telemetry to Google Cloud: + +```java +FirebasePlugin.builder() + .projectId("my-project") + .enableTelemetry(true) // Cloud Trace, Monitoring, Logging + .forceDevExport(true) // Also export in dev mode + .build() +``` diff --git a/docs/src/content/docs/overview.mdx b/docs/src/content/docs/overview.mdx new file mode 100644 index 000000000..efd5c742d --- /dev/null +++ b/docs/src/content/docs/overview.mdx @@ -0,0 +1,59 @@ +--- +title: Overview +description: An overview of Genkit for Java — the Java implementation of the Genkit AI framework. +--- + +Genkit for Java is the Java implementation of the [Genkit](https://genkit.dev) framework for building AI-powered applications. + +:::caution[Status] +Currently in active development (`1.0.0-SNAPSHOT`). Requires **Java 21+**. +::: + +:::note +This is an **unofficial** community-driven Java port of Google's Genkit framework. +::: + +## What is Genkit? + +Genkit is an open-source framework that helps you build AI-powered applications. It provides: + +- **Unified model API** — Call any supported AI model through a single, consistent interface +- **Flows** — Define observable, traceable AI workflows that can be exposed as HTTP endpoints +- **Tools** — Let models call your functions during generation +- **DotPrompt** — Manage prompts as template files with Handlebars support +- **Structured Output** — Generate type-safe outputs with automatic JSON schema generation +- **RAG** — Build retrieval-augmented generation pipelines with indexers and retrievers +- **Evaluations** — Assess AI output quality with custom and pre-built evaluators +- **Observability** — Full OpenTelemetry integration for tracing and metrics +- **Dev UI** — Visual tool for testing and debugging your AI logic + +## Supported Providers + +The Java SDK supports a broad range of providers: + +| Category | Providers | +|----------|-----------| +| **AI Models** | OpenAI, Google GenAI (Gemini), Anthropic (Claude), AWS Bedrock, Azure AI Foundry, xAI (Grok), DeepSeek, Cohere, Mistral, Groq, Ollama, any OpenAI-compatible endpoint | +| **Vector Databases** | Weaviate, PostgreSQL (pgvector), Pinecone, Firebase Firestore, Local file-based | +| **Server Frameworks** | Jetty, Spring Boot | +| **Integrations** | Firebase (Firestore, Cloud Functions, Telemetry), MCP, Pre-built evaluators | + +## Modules + +| Module | Description | +|--------|-------------| +| **genkit-core** | Core framework: actions, flows, registry, tracing (OpenTelemetry) | +| **genkit-ai** | AI abstractions: models, embedders, tools, prompts, retrievers, indexers, evaluators | +| **genkit** | Main entry point combining core and AI with reflection server | +| **plugins/** | 20+ plugin implementations for models, databases, servers, and integrations | + +## Next Steps + +import { LinkCard, CardGrid } from '@astrojs/starlight/components'; + + + + + + + diff --git a/docs/src/content/docs/plugins/anthropic.md b/docs/src/content/docs/plugins/anthropic.md new file mode 100644 index 000000000..d7ce87b87 --- /dev/null +++ b/docs/src/content/docs/plugins/anthropic.md @@ -0,0 +1,63 @@ +--- +title: Anthropic (Claude) +description: Use Anthropic's Claude models for text generation. +--- + +The Anthropic plugin provides access to Claude models. + +## Installation + +```xml + + com.google.genkit + genkit-plugin-anthropic + 1.0.0-SNAPSHOT + +``` + +## Configuration + +```bash +export ANTHROPIC_API_KEY=your-api-key +``` + +## Usage + +```java +import com.google.genkit.plugins.anthropic.AnthropicPlugin; + +Genkit genkit = Genkit.builder() + .plugin(AnthropicPlugin.create()) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("anthropic/claude-sonnet-4-5-20250929") + .prompt("Tell me about AI") + .build()); +``` + +## Available models + +### Claude 4.5 family +- `anthropic/claude-opus-4-5-20251101` +- `anthropic/claude-sonnet-4-5-20250929` +- `anthropic/claude-haiku-4-5-20251001` + +### Claude 4 family +- `anthropic/claude-4-*` + +### Claude 3 family +- `anthropic/claude-3-opus-*` +- `anthropic/claude-3-sonnet-*` +- `anthropic/claude-3-haiku-*` + +## Features + +- Text generation +- Streaming +- Tool calling + +## Sample + +See the [anthropic sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/anthropic). diff --git a/docs/src/content/docs/plugins/aws-bedrock.md b/docs/src/content/docs/plugins/aws-bedrock.md new file mode 100644 index 000000000..a3414a19b --- /dev/null +++ b/docs/src/content/docs/plugins/aws-bedrock.md @@ -0,0 +1,62 @@ +--- +title: AWS Bedrock +description: Access 90+ models through AWS Bedrock including Nova, Claude, Llama, and Mistral. +--- + +The AWS Bedrock plugin provides access to 90+ AI models available on AWS Bedrock. + +## Installation + +```xml + + com.google.genkit + genkit-plugin-aws-bedrock + 1.0.0-SNAPSHOT + +``` + +## Configuration + +Uses the AWS Default Credentials Provider Chain. Configure credentials via environment variables, shared credentials file, or IAM roles. + +```bash +export AWS_ACCESS_KEY_ID=your-key +export AWS_SECRET_ACCESS_KEY=your-secret +export AWS_REGION=us-east-1 +``` + +## Usage + +```java +import com.google.genkit.plugins.awsbedrock.AwsBedrockPlugin; + +Genkit genkit = Genkit.builder() + .plugin(AwsBedrockPlugin.create()) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("aws-bedrock/amazon.nova-pro-v1:0") + .prompt("Tell me about AI") + .build()); +``` + +## Available model families + +- **Amazon Nova** — Nova Pro, Nova Lite, Nova Micro +- **Anthropic Claude** — Claude 4.5, Claude 4, Claude 3 families +- **Meta Llama** — Llama 3, Llama 4 +- **Mistral** — Mistral Large, Small, Mixtral +- **Cohere** — Command R+, Command R +- **OpenAI** — Via Bedrock marketplace +- And many more... + +## Features + +- Multi-provider model access +- INFERENCE_PROFILE support for advanced models +- Text generation, streaming, tool calling + +## Sample + +See the [aws-bedrock sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/aws-bedrock). diff --git a/docs/src/content/docs/plugins/azure-foundry.md b/docs/src/content/docs/plugins/azure-foundry.md new file mode 100644 index 000000000..87d1a7471 --- /dev/null +++ b/docs/src/content/docs/plugins/azure-foundry.md @@ -0,0 +1,52 @@ +--- +title: Azure AI Foundry +description: Access Azure-hosted AI models including GPT-5, o1, Llama, and more. +--- + +The Azure AI Foundry plugin provides access to models hosted on Azure. + +## Installation + +```xml + + com.google.genkit + genkit-plugin-azure-foundry + 1.0.0-SNAPSHOT + +``` + +## Configuration + +```bash +export AZURE_AI_FOUNDRY_ENDPOINT=https://your-endpoint.inference.ai.azure.com +export AZURE_AI_FOUNDRY_API_KEY=your-api-key +``` + +Alternatively, supports Azure Managed Identity authentication. + +## Usage + +```java +import com.google.genkit.plugins.azurefoundry.AzureFoundryPlugin; + +Genkit genkit = Genkit.builder() + .plugin(AzureFoundryPlugin.create()) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("azure-foundry/gpt-4o") + .prompt("Tell me about AI") + .build()); +``` + +## Available models + +- GPT-5, GPT-4o, o1, o3-mini +- Grok, Llama, DeepSeek +- Claude 4.x via Azure marketplace +- And many more + +## Sample + +See the [azure-foundry sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/azure-foundry). diff --git a/docs/src/content/docs/plugins/cohere.md b/docs/src/content/docs/plugins/cohere.md new file mode 100644 index 000000000..00f222be1 --- /dev/null +++ b/docs/src/content/docs/plugins/cohere.md @@ -0,0 +1,51 @@ +--- +title: Cohere +description: Use Cohere Command models for text generation. +--- + +## Installation + +```xml + + com.google.genkit + genkit-plugin-cohere + 1.0.0-SNAPSHOT + +``` + +## Configuration + +```bash +export COHERE_API_KEY=your-api-key +``` + +## Usage + +```java +import com.google.genkit.plugins.cohere.CoherePlugin; + +Genkit genkit = Genkit.builder() + .plugin(CoherePlugin.create()) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("cohere/command-a-03-2025") + .prompt("Tell me about AI") + .build()); +``` + +## Available models + +- `cohere/command-a-03-2025` +- `cohere/command-r7b-12-2024` +- `cohere/command-r-08-2024` +- `cohere/command-r-plus-08-2024` + +## Features + +- Text generation, tool calling, RAG support, SSE streaming + +## Sample + +See the [cohere sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/cohere). diff --git a/docs/src/content/docs/plugins/compat-oai.md b/docs/src/content/docs/plugins/compat-oai.md new file mode 100644 index 000000000..caa9b9112 --- /dev/null +++ b/docs/src/content/docs/plugins/compat-oai.md @@ -0,0 +1,48 @@ +--- +title: OpenAI-Compatible (compat-oai) +description: Connect to any OpenAI-compatible API endpoint. +--- + +The compat-oai plugin is a base implementation for connecting to any endpoint that follows the OpenAI API format. + +## Installation + +```xml + + com.google.genkit + genkit-plugin-compat-oai + 1.0.0-SNAPSHOT + +``` + +## Usage + +Connect to custom OpenAI-compatible endpoints: + +```java +import com.google.genkit.plugins.compatoai.CompatOaiPlugin; + +Genkit genkit = Genkit.builder() + .plugin(CompatOaiPlugin.create( + "https://your-custom-endpoint.com/v1", + "your-api-key")) + .build(); +``` + +## Built on by other plugins + +This plugin serves as the base for several other plugins: + +- **xAI** (Grok) +- **DeepSeek** +- **Mistral** +- **Cohere** +- **Groq** +- **OpenAI** + +## Features + +- Query parameter support (for Azure OpenAI, versioning, etc.) +- Tool calling +- Streaming +- Compatible with self-hosted LLM endpoints (vLLM, LiteLLM, etc.) diff --git a/docs/src/content/docs/plugins/deepseek.md b/docs/src/content/docs/plugins/deepseek.md new file mode 100644 index 000000000..a19a0b247 --- /dev/null +++ b/docs/src/content/docs/plugins/deepseek.md @@ -0,0 +1,49 @@ +--- +title: DeepSeek +description: Use DeepSeek models for text generation and reasoning. +--- + +## Installation + +```xml + + com.google.genkit + genkit-plugin-deepseek + 1.0.0-SNAPSHOT + +``` + +## Configuration + +```bash +export DEEPSEEK_API_KEY=your-api-key +``` + +## Usage + +```java +import com.google.genkit.plugins.deepseek.DeepSeekPlugin; + +Genkit genkit = Genkit.builder() + .plugin(DeepSeekPlugin.create()) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("deepseek/deepseek-chat") + .prompt("Tell me about AI") + .build()); +``` + +## Available models + +- `deepseek/deepseek-chat` — General chat model +- `deepseek/deepseek-reasoner` — Reasoning model + +## Features + +- Text generation, streaming, tool calling, RAG + +## Sample + +See the [deepseek sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/deepseek). diff --git a/docs/src/content/docs/plugins/evaluators.md b/docs/src/content/docs/plugins/evaluators.md new file mode 100644 index 000000000..940b8c797 --- /dev/null +++ b/docs/src/content/docs/plugins/evaluators.md @@ -0,0 +1,62 @@ +--- +title: Evaluators Plugin +description: Pre-built RAGAS-style evaluators for assessing AI output quality. +--- + +## Installation + +```xml + + com.google.genkit + genkit-plugin-evaluators + 1.0.0-SNAPSHOT + +``` + +## Usage + +```java +import com.google.genkit.plugins.evaluators.EvaluatorsPlugin; +import com.google.genkit.plugins.evaluators.EvaluatorsPluginOptions; +import com.google.genkit.plugins.evaluators.GenkitMetric; + +Genkit genkit = Genkit.builder() + .plugin(OpenAIPlugin.create()) + .plugin(EvaluatorsPlugin.create( + EvaluatorsPluginOptions.builder() + .judge("openai/gpt-4o-mini") + .metrics(List.of( + GenkitMetric.FAITHFULNESS, + GenkitMetric.ANSWER_RELEVANCY, + GenkitMetric.ANSWER_ACCURACY, + GenkitMetric.MALICIOUSNESS, + GenkitMetric.REGEX, + GenkitMetric.DEEP_EQUAL, + GenkitMetric.JSONATA + )) + .build())) + .build(); +``` + +## Available metrics + +### LLM-based metrics (require a judge model) + +| Metric | Description | +|--------|-------------| +| `FAITHFULNESS` | Factual accuracy against provided context | +| `ANSWER_RELEVANCY` | Answer pertains to the question | +| `ANSWER_ACCURACY` | Matches reference answer | +| `MALICIOUSNESS` | Detects harmful content | + +### Free metrics (no LLM required) + +| Metric | Description | +|--------|-------------| +| `REGEX` | Pattern matching | +| `DEEP_EQUAL` | JSON deep equality comparison | +| `JSONATA` | JSONata expression evaluation | + +## Sample + +See the [evaluators-plugin sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/evaluators-plugin). diff --git a/docs/src/content/docs/plugins/firebase-functions.md b/docs/src/content/docs/plugins/firebase-functions.md new file mode 100644 index 000000000..57f1505ee --- /dev/null +++ b/docs/src/content/docs/plugins/firebase-functions.md @@ -0,0 +1,123 @@ +--- +title: Firebase Cloud Functions +description: Deploy Genkit flows as Firebase Cloud Functions with authentication, App Check, and streaming support. +--- + +Expose Genkit flows as Firebase Cloud Functions using `OnCallGenkit`. This lets you deploy AI-powered functions that scale automatically on Google Cloud. + +## Installation + +```xml + + com.google.genkit + genkit-plugin-firebase + 1.0.0-SNAPSHOT + +``` + +You also need the Cloud Functions Framework: + +```xml + + com.google.cloud.functions + functions-framework-api + 1.1.0 + provided + +``` + +## Creating a Cloud Function + +```java +import com.google.cloud.functions.HttpFunction; +import com.google.cloud.functions.HttpRequest; +import com.google.cloud.functions.HttpResponse; +import com.google.genkit.Genkit; +import com.google.genkit.ai.GenerateOptions; +import com.google.genkit.plugins.firebase.FirebasePlugin; +import com.google.genkit.plugins.firebase.functions.OnCallGenkit; +import com.google.genkit.plugins.googlegenai.GoogleGenAIPlugin; + +public class GeneratePoemFunction implements HttpFunction { + private final OnCallGenkit genkitFunction; + + public GeneratePoemFunction() { + Genkit genkit = Genkit.builder() + .plugin(GoogleGenAIPlugin.create(System.getenv("GEMINI_API_KEY"))) + .plugin(FirebasePlugin.builder().build()) + .build(); + + genkit.defineFlow("generatePoem", String.class, String.class, + (ctx, topic) -> genkit.generate(GenerateOptions.builder() + .model("googleai/gemini-2.0-flash") + .prompt("Write a poem about: " + topic) + .build()).getText()); + + this.genkitFunction = OnCallGenkit.fromFlow(genkit, "generatePoem"); + } + + @Override + public void service(HttpRequest request, HttpResponse response) + throws IOException { + genkitFunction.service(request, response); + } +} +``` + +## Authentication and App Check + +`OnCallGenkit` supports Firebase Authentication and App Check for securing your functions: + +```java +this.genkitFunction = OnCallGenkit.fromFlow(genkit, "generatePoem") + .withAuthPolicy(authContext -> { + if (authContext.getUid() == null) { + throw new RuntimeException("Authentication required"); + } + }) + .enforceAppCheck(true) + .consumeAppCheckToken(true) + .withCors("https://myapp.example.com"); +``` + +### Auth policy options + +| Method | Description | +|--------|-------------| +| `withAuthPolicy(policy)` | Validate the Firebase Auth context before running the flow | +| `enforceAppCheck(true)` | Reject requests without a valid App Check token | +| `consumeAppCheckToken(true)` | Consume the App Check token (replay protection) | +| `withCors(origin)` | Set allowed CORS origins | + +## Streaming responses + +`OnCallGenkit` automatically supports streaming via Server-Sent Events (SSE). Clients that send requests with `Accept: text/event-stream` receive chunked responses. + +## Deploying + +Deploy with the Google Cloud CLI: + +```bash +gcloud functions deploy generatePoem \ + --runtime java21 \ + --trigger-http \ + --entry-point=com.example.GeneratePoemFunction \ + --set-env-vars GEMINI_API_KEY=your-key +``` + +Or deploy with the Firebase CLI: + +```bash +firebase deploy --only functions +``` + +## Requirements + +- Firebase project on the Blaze (pay-as-you-go) plan +- Application Default Credentials or a service account JSON +- `GCLOUD_PROJECT` or `GOOGLE_CLOUD_PROJECT` environment variable (auto-detected) +- Java 21 runtime + +## Sample + +See the [firebase sample](https://github.com/xavidop/genkit-java/tree/main/samples/firebase) for a Cloud Functions deployment example. diff --git a/docs/src/content/docs/plugins/firebase-vector-store.md b/docs/src/content/docs/plugins/firebase-vector-store.md new file mode 100644 index 000000000..c9bc5031c --- /dev/null +++ b/docs/src/content/docs/plugins/firebase-vector-store.md @@ -0,0 +1,123 @@ +--- +title: Firebase Vector Store +description: Use Firestore native vector similarity search for RAG applications. +--- + +Firestore supports native vector similarity search, making it a fully managed vector store for RAG applications. The Firebase plugin automatically handles embedding generation, document indexing, and vector queries. + +## Installation + +```xml + + com.google.genkit + genkit-plugin-firebase + 1.0.0-SNAPSHOT + +``` + +## Setting up a retriever + +```java +import com.google.genkit.plugins.firebase.FirebasePlugin; +import com.google.genkit.plugins.firebase.retriever.FirestoreRetrieverConfig; + +Genkit genkit = Genkit.builder() + .plugin(GoogleGenAIPlugin.create(apiKey)) + .plugin(FirebasePlugin.builder() + .projectId("my-project") + .addRetriever(FirestoreRetrieverConfig.builder() + .name("my-docs") + .collection("documents") + .embedderName("googleai/text-embedding-004") + .vectorField("embedding") + .contentField("content") + .distanceMeasure(FirestoreRetrieverConfig.DistanceMeasure.COSINE) + .defaultLimit(5) + .build()) + .build()) + .build(); +``` + +## Indexing documents + +Documents are embedded automatically when indexed. The plugin generates embeddings using the configured embedder and stores them alongside the content in Firestore. + +```java +List docs = List.of( + Document.fromText("Genkit is a framework for building AI apps"), + Document.fromText("Firebase provides cloud services for apps") +); +genkit.index("firebase/my-docs", docs); +``` + +## Retrieving with vector similarity + +```java +List results = genkit.retrieve("firebase/my-docs", "What is Genkit?"); +``` + +## Using results in RAG + +```java +genkit.defineFlow("ragQuery", String.class, String.class, (ctx, question) -> { + List context = genkit.retrieve("firebase/my-docs", question); + return genkit.generate(GenerateOptions.builder() + .model("googleai/gemini-2.0-flash") + .prompt(question) + .docs(context) + .build()).getText(); +}); +``` + +## Auto-creating database and vector index + +The plugin can automatically create the Firestore database and the required composite vector index: + +```java +FirestoreRetrieverConfig.builder() + .name("my-docs") + .collection("documents") + .embedderName("googleai/text-embedding-004") + .vectorField("embedding") + .contentField("content") + .createDatabaseIfNotExists(true) + .createVectorIndexIfNotExists(true) + .build() +``` + +You can also create the vector index manually via the Firebase CLI: + +```bash +gcloud firestore indexes composite create \ + --collection-group=documents \ + --field-config=field-path=embedding,vector-config='{"dimension":"768","flat":{}}' \ + --database="(default)" +``` + +## Configuration options + +| Option | Default | Description | +|--------|---------|-------------| +| `name` | (required) | Retriever name — used as `firebase/{name}` | +| `collection` | (required) | Firestore collection name | +| `embedderName` | (required) | Embedder to use (e.g., `googleai/text-embedding-004`) | +| `vectorField` | `"embedding"` | Firestore field storing the vector | +| `contentField` | `"content"` | Firestore field storing the text content | +| `distanceMeasure` | `COSINE` | `COSINE`, `EUCLIDEAN`, or `DOT_PRODUCT` | +| `defaultLimit` | `10` | Number of results to return | +| `distanceThreshold` | none | Maximum distance threshold | +| `metadataFields` | none | Additional fields to include in document metadata | +| `embedderDimension` | `768` | Embedding vector dimension | +| `databaseId` | `"(default)"` | Firestore database ID | +| `createDatabaseIfNotExists` | `false` | Auto-create Firestore database | +| `createVectorIndexIfNotExists` | `false` | Auto-create composite vector index | + +## Requirements + +- Firebase project on the Blaze (pay-as-you-go) plan +- Application Default Credentials or a service account JSON +- `GCLOUD_PROJECT` or `GOOGLE_CLOUD_PROJECT` environment variable (auto-detected) + +## Sample + +See the [firebase sample](https://github.com/xavidop/genkit-java/tree/main/samples/firebase) for a complete RAG pipeline with Firestore vector search. diff --git a/docs/src/content/docs/plugins/firebase.md b/docs/src/content/docs/plugins/firebase.md new file mode 100644 index 000000000..a56baa068 --- /dev/null +++ b/docs/src/content/docs/plugins/firebase.md @@ -0,0 +1,49 @@ +--- +title: Firebase +description: Firebase integration with Firestore vector search, Cloud Functions deployment, and Google Cloud telemetry. +--- + +The Firebase plugin provides three key capabilities: + +- **[Firestore Vector Store](/genkit-java/plugins/firebase-vector-store/)** — Native vector similarity search for RAG applications. +- **[Cloud Functions Deployment](/genkit-java/plugins/firebase-functions/)** — Deploy Genkit flows as scalable Cloud Functions with auth and streaming. +- **Telemetry** — Export traces and metrics to Google Cloud observability. + +## Installation + +```xml + + com.google.genkit + genkit-plugin-firebase + 1.0.0-SNAPSHOT + +``` + +## Telemetry + +Export traces and metrics to Google Cloud observability (Cloud Trace, Monitoring, Logging): + +```java +FirebasePlugin.builder() + .projectId("my-project") + .enableTelemetry(true) + .forceDevExport(true) // Also export in dev mode + .build() +``` + +### Required GCP APIs + +Enable these APIs in your Google Cloud project: + +- Cloud Trace API +- Cloud Monitoring API + +## Requirements + +- Firebase project on the Blaze (pay-as-you-go) plan +- Application Default Credentials or a service account JSON +- `GCLOUD_PROJECT` or `GOOGLE_CLOUD_PROJECT` environment variable (auto-detected) + +## Sample + +See the [firebase sample](https://github.com/xavidop/genkit-java/tree/main/samples/firebase) for a complete RAG pipeline with Firestore vector search and a Cloud Functions deployment example. diff --git a/docs/src/content/docs/plugins/google-genai.md b/docs/src/content/docs/plugins/google-genai.md new file mode 100644 index 000000000..1f32e9a15 --- /dev/null +++ b/docs/src/content/docs/plugins/google-genai.md @@ -0,0 +1,172 @@ +--- +title: Google GenAI (Gemini) +description: Use Google Gemini models, embeddings, Imagen image generation, TTS, and Veo video generation. +--- + +The Google GenAI plugin provides access to Google's Gemini models, text embeddings, Imagen image generation, text-to-speech, and Veo video generation. + +## Installation + +```xml + + com.google.genkit + genkit-plugin-google-genai + 1.0.0-SNAPSHOT + +``` + +## Configuration + +```bash +export GOOGLE_GENAI_API_KEY=your-api-key +``` + +Get an API key from [Google AI Studio](https://aistudio.google.com/apikey). + +### Vertex AI + +To use Vertex AI instead of the Google AI Developer API: + +```bash +export GOOGLE_GENAI_USE_VERTEXAI=true +export GOOGLE_CLOUD_PROJECT=my-project +export GOOGLE_CLOUD_LOCATION=us-central1 # optional, defaults to us-central1 +``` + +## Usage + +```java +import com.google.genkit.plugins.googlegenai.GoogleGenAIPlugin; + +Genkit genkit = Genkit.builder() + .plugin(GoogleGenAIPlugin.create(System.getenv("GOOGLE_GENAI_API_KEY"))) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("googleai/gemini-2.0-flash") + .prompt("Tell me about AI") + .build()); +``` + +## Embeddings + +Generate text embeddings for RAG, semantic search, and similarity tasks: + +```java +import com.google.genkit.ai.EmbedResponse; +import com.google.genkit.ai.Document; + +List documents = List.of( + Document.fromText("Genkit is a framework for building AI apps"), + Document.fromText("Firebase provides cloud services") +); + +EmbedResponse response = genkit.embed("googleai/text-embedding-004", documents); + +// Access embedding vectors +float[] vector = response.getEmbeddings().get(0).getValues(); +// vector.length == 768 +``` + +Embeddings are used automatically by vector store plugins (Firebase, Pinecone, pgvector, etc.) when you configure an embedder name. You can also use them directly for custom similarity search. + +### Embedding options + +You can pass task-specific options to optimize embedding quality: + +```java +Map embedOptions = Map.of( + "taskType", "RETRIEVAL_DOCUMENT", // or "RETRIEVAL_QUERY", "SEMANTIC_SIMILARITY" + "title", "Document title", + "outputDimensionality", 256 // reduce dimensions if needed +); +``` + +## Text-to-Speech (TTS) + +Generate natural-sounding speech from text using Gemini TTS models: + +```java +Map ttsOptions = Map.of("voiceName", "Zephyr"); +GenerationConfig config = GenerationConfig.builder() + .custom(ttsOptions) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("googleai/gemini-2.5-flash-preview-tts") + .prompt("Hello! Welcome to Genkit Java.") + .config(config) + .build()); + +// The response contains audio as a media part (WAV format, base64-encoded) +String audioDataUrl = response.getMessage().getParts().get(0).getMedia().getUrl(); +// "data:audio/wav;base64,..." +``` + +### Saving audio to a file + +```java +String dataUrl = response.getMessage().getParts().get(0).getMedia().getUrl(); +String base64Data = dataUrl.substring(dataUrl.indexOf(",") + 1); +byte[] audioBytes = Base64.getDecoder().decode(base64Data); +Files.write(Path.of("output.wav"), audioBytes); +``` + +## Video Generation (Veo) + +Generate videos from text prompts or images using Google's Veo models: + +```java +Map veoOptions = Map.of( + "numberOfVideos", 1, + "durationSeconds", 8, + "aspectRatio", "16:9", + "timeoutMs", 600000 // 10 min — video generation can take a while +); +GenerationConfig config = GenerationConfig.builder() + .custom(veoOptions) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("googleai/veo-3.0-generate-001") + .prompt("A serene Japanese garden with cherry blossoms falling") + .config(config) + .build()); + +// The response contains video as a media part (base64-encoded) +String videoDataUrl = response.getMessage().getParts().get(0).getMedia().getUrl(); +``` + +:::note +Video generation is an asynchronous operation. The plugin automatically polls until the video is ready. This can take several minutes depending on the model and settings. +::: + + +## Image Generation (Imagen) + +Generate images with Imagen: + +```java +Map imagenOptions = Map.of( + "numberOfImages", 1, + "aspectRatio", "1:1" +); +GenerationConfig config = GenerationConfig.builder() + .custom(imagenOptions) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("googleai/imagen-4.0-fast-generate-001") + .prompt("A cat wearing a space suit") + .config(config) + .build()); +``` + + +## Sample + +See the [google-genai sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/google-genai) for complete examples of text generation, tool calling, embeddings, image generation, TTS, and video generation. diff --git a/docs/src/content/docs/plugins/groq.md b/docs/src/content/docs/plugins/groq.md new file mode 100644 index 000000000..8088038f2 --- /dev/null +++ b/docs/src/content/docs/plugins/groq.md @@ -0,0 +1,53 @@ +--- +title: Groq +description: Ultra-fast inference with Groq for Llama, Mixtral, and more. +--- + +## Installation + +```xml + + com.google.genkit + genkit-plugin-groq + 1.0.0-SNAPSHOT + +``` + +## Configuration + +```bash +export GROQ_API_KEY=your-api-key +``` + +## Usage + +```java +import com.google.genkit.plugins.groq.GroqPlugin; + +Genkit genkit = Genkit.builder() + .plugin(GroqPlugin.create()) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("groq/llama-3.3-70b-versatile") + .prompt("Tell me about AI") + .build()); +``` + +## Available models + +| Model | Speed | +|-------|-------| +| `groq/llama-3.1-8b-instant` | ~1200 tokens/sec | +| `groq/llama-3.3-70b-versatile` | ~560 tokens/sec | +| `groq/openai/gpt-oss` models | Varies | +| `groq/meta-llama/llama-guard-4` | Content moderation | + +## Features + +- Ultra-fast inference, text generation, streaming, tool calling, RAG, content moderation + +## Sample + +See the [groq sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/groq). diff --git a/docs/src/content/docs/plugins/jetty.md b/docs/src/content/docs/plugins/jetty.md new file mode 100644 index 000000000..681e42247 --- /dev/null +++ b/docs/src/content/docs/plugins/jetty.md @@ -0,0 +1,48 @@ +--- +title: Jetty Server +description: Expose Genkit flows as HTTP endpoints using Jetty 12. +--- + +## Installation + +```xml + + com.google.genkit + genkit-plugin-jetty + 1.0.0-SNAPSHOT + +``` + +## Usage + +```java +import com.google.genkit.plugins.jetty.JettyPlugin; +import com.google.genkit.plugins.jetty.JettyPluginOptions; + +JettyPlugin jetty = new JettyPlugin(JettyPluginOptions.builder() + .port(8080) + .build()); + +Genkit genkit = Genkit.builder() + .plugin(jetty) + .build(); + +// Define your flows here... + +// Start the server (blocks until stopped) +jetty.start(); +``` + +:::caution +You must call `jetty.start()` after building the `Genkit` instance and defining your flows. Without it, the HTTP server will not start and your flows won't be accessible. +::: + +All defined flows are automatically exposed as HTTP endpoints. + +## Calling flows via HTTP + +```bash +curl -X POST http://localhost:8080/api/flows/tellJoke \ + -H "Content-Type: application/json" \ + -d '{"data": "pirates"}' +``` diff --git a/docs/src/content/docs/plugins/localvec.md b/docs/src/content/docs/plugins/localvec.md new file mode 100644 index 000000000..e4ca24218 --- /dev/null +++ b/docs/src/content/docs/plugins/localvec.md @@ -0,0 +1,38 @@ +--- +title: Local Vector Store +description: File-based vector store for local development and testing. +--- + +## Installation + +```xml + + com.google.genkit + genkit-plugin-localvec + 1.0.0-SNAPSHOT + +``` + +## Usage + +The local vector store is ideal for development and testing. It stores vectors in local files. + +```java +import com.google.genkit.plugins.localvec.LocalVecPlugin; + +Genkit genkit = Genkit.builder() + .plugin(LocalVecPlugin.create()) + .build(); +``` + +## When to use + +- Local development and prototyping +- Unit testing RAG pipelines +- Quick experiments without external dependencies + +For production, use [Weaviate](/genkit-java/plugins/weaviate/), [PostgreSQL](/genkit-java/plugins/postgresql/), [Pinecone](/genkit-java/plugins/pinecone/), or [Firebase Firestore](/genkit-java/plugins/firebase/). + +## Sample + +See the [rag sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/rag). diff --git a/docs/src/content/docs/plugins/mcp.md b/docs/src/content/docs/plugins/mcp.md new file mode 100644 index 000000000..26cd5e194 --- /dev/null +++ b/docs/src/content/docs/plugins/mcp.md @@ -0,0 +1,235 @@ +--- +title: MCP (Model Context Protocol) +description: Connect to MCP servers as a client or expose Genkit tools as an MCP server. +--- + +The MCP plugin supports both sides of the [Model Context Protocol](https://modelcontextprotocol.io/): + +- **Client** — Connect your Genkit app to external MCP servers (filesystem, databases, APIs) and use their tools with AI models. +- **Server** — Expose your Genkit tools to external MCP clients like Claude Desktop. + +## Installation + +```xml + + com.google.genkit + genkit-plugin-mcp + 1.0.0-SNAPSHOT + +``` + +--- + +## MCP Client — Connecting to external servers + +Use `MCPPlugin` to connect your Genkit app to one or more external MCP servers. The plugin automatically discovers tools from each server and makes them available as Genkit tools. + +### STDIO transport (local processes) + +Connect to MCP servers that run as local processes communicating via stdin/stdout: + +```java +import com.google.genkit.plugins.mcp.MCPPlugin; +import com.google.genkit.plugins.mcp.MCPPluginOptions; +import com.google.genkit.plugins.mcp.MCPServerConfig; + +MCPPlugin mcpPlugin = new MCPPlugin(MCPPluginOptions.builder() + .addServer("filesystem", MCPServerConfig.stdio( + "npx", "-y", "@modelcontextprotocol/server-filesystem", "/tmp")) + .build()); + +Genkit genkit = Genkit.builder() + .plugin(mcpPlugin) + .build(); +``` + +### HTTP/SSE transport (remote servers) + +Connect to remote MCP servers over HTTP with Server-Sent Events: + +```java +MCPPlugin mcpPlugin = new MCPPlugin(MCPPluginOptions.builder() + .addServer("my-server", MCPServerConfig.http("https://my-mcp-server.com/sse")) + .build()); +``` + +### Streamable HTTP transport + +For servers supporting the modern Streamable HTTP transport: + +```java +MCPPlugin mcpPlugin = new MCPPlugin(MCPPluginOptions.builder() + .addServer("my-server", MCPServerConfig.streamableHttp("https://my-mcp-server.com/mcp")) + .build()); +``` + +### Using MCP tools with AI models + +Once connected, MCP tools are automatically registered as Genkit tools. AI models can use them directly: + +```java +ModelResponse response = genkit.generate(GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("List all files in the /tmp directory") + .tools("filesystem/list_directory", "filesystem/read_file") + .build()); +``` + +### Convenience factory methods + +For quick single-server setups: + +```java +// STDIO +Genkit genkit = Genkit.builder() + .plugin(MCPPlugin.stdio("filesystem", + "npx", "-y", "@modelcontextprotocol/server-filesystem", "/tmp")) + .build(); + +// HTTP +Genkit genkit = Genkit.builder() + .plugin(MCPPlugin.http("my-server", "https://my-mcp-server.com/sse")) + .build(); +``` + +### Multi-server setup + +Connect to multiple MCP servers simultaneously: + +```java +MCPPlugin mcpPlugin = new MCPPlugin(MCPPluginOptions.builder() + .addServer("filesystem", MCPServerConfig.stdio( + "npx", "-y", "@modelcontextprotocol/server-filesystem", "/tmp")) + .addServer("database", MCPServerConfig.http("https://db-mcp-server.com/sse")) + .addServer("github", MCPServerConfig.stdio( + "npx", "-y", "@modelcontextprotocol/server-github")) + .build()); +``` + +Tools from each server are namespaced: `filesystem/read_file`, `database/query`, `github/list_repos`. + +### Direct tool invocation + +Call MCP tools directly without going through an AI model: + +```java +Object result = mcpPlugin.callTool("filesystem", "read_file", + Map.of("path", "/tmp/example.txt")); +``` + +### Accessing MCP resources + +Read resources exposed by MCP servers: + +```java +List resources = mcpPlugin.getResources("filesystem"); + +MCPResourceContent content = mcpPlugin.readResource("filesystem", + "file:///tmp/example.txt"); +``` + +### Client configuration options + +| Option | Default | Description | +|--------|---------|-------------| +| `name` | `"genkit-mcp"` | Client name for identification | +| `requestTimeout` | 30 seconds | Timeout per MCP request | +| `rawToolResponses` | `false` | Return raw MCP responses | + +### Cleanup + +Disconnect from all MCP servers when done: + +```java +mcpPlugin.disconnect(); +``` + +--- + +## MCP Server — Exposing Genkit tools + +Use `MCPServer` to expose your Genkit tools to external MCP clients (like Claude Desktop, Cursor, or other MCP-compatible apps). + +### Creating an MCP server + +```java +import com.google.genkit.Genkit; +import com.google.genkit.plugins.mcp.MCPServer; +import com.google.genkit.plugins.mcp.MCPServerOptions; + +Genkit genkit = Genkit.builder().build(); + +// Define tools +genkit.defineTool("calculator", "Performs arithmetic", + Map.of("expression", String.class), String.class, + (ctx, input) -> { + // evaluate expression... + return result; + }); + +genkit.defineTool("get_weather", "Gets current weather", + Map.of("city", String.class), String.class, + (ctx, input) -> { + return "Sunny, 22°C in " + input.get("city"); + }); + +// Create and start the MCP server +MCPServer server = new MCPServer(genkit.getRegistry(), + MCPServerOptions.builder() + .name("my-genkit-tools") + .version("1.0.0") + .build()); + +// Start with STDIO transport (default) +server.start(); +``` + +The server automatically discovers all tools registered in the Genkit registry and exposes them via the MCP protocol. + +### Using with Claude Desktop + +Add this to your Claude Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS): + +```json +{ + "mcpServers": { + "genkit-tools": { + "command": "java", + "args": ["-jar", "/path/to/your-app.jar"] + } + } +} +``` + +### Custom transport + +By default, the server uses STDIO transport. You can provide a custom transport provider: + +```java +server.start(customTransportProvider); +``` + +### Server options + +| Option | Default | Description | +|--------|---------|-------------| +| `name` | `"genkit-mcp-server"` | Server name exposed to clients | +| `version` | `"1.0.0"` | Server version | + +--- + +## Popular MCP servers + +| Server | Install | Description | +|--------|---------|-------------| +| Filesystem | `npx @modelcontextprotocol/server-filesystem` | Read/write local files | +| GitHub | `npx @modelcontextprotocol/server-github` | GitHub API access | +| Postgres | `npx @modelcontextprotocol/server-postgres` | PostgreSQL queries | +| Brave Search | `npx @modelcontextprotocol/server-brave-search` | Web search | +| Everything | `npx @modelcontextprotocol/server-everything` | Demo/test server | + +Browse more at [mcp.so/servers](https://mcp.so/servers). + +## Sample + +See the [mcp sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/mcp) for client and server examples. diff --git a/docs/src/content/docs/plugins/mistral.md b/docs/src/content/docs/plugins/mistral.md new file mode 100644 index 000000000..79ca024fa --- /dev/null +++ b/docs/src/content/docs/plugins/mistral.md @@ -0,0 +1,54 @@ +--- +title: Mistral +description: Use Mistral AI models for text generation and code. +--- + +## Installation + +```xml + + com.google.genkit + genkit-plugin-mistral + 1.0.0-SNAPSHOT + +``` + +## Configuration + +```bash +export MISTRAL_API_KEY=your-api-key +``` + +## Usage + +```java +import com.google.genkit.plugins.mistral.MistralPlugin; + +Genkit genkit = Genkit.builder() + .plugin(MistralPlugin.create()) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("mistral/mistral-large-3-25-12") + .prompt("Tell me about AI") + .build()); +``` + +## Available models + +| Model | Context | +|-------|---------| +| `mistral/mistral-large-3-25-12` | 128K | +| `mistral/mistral-medium-3-1-25-08` | 128K | +| `mistral/mistral-small-*` | 128K | +| `mistral/ministral-*` | 128K | +| `mistral/codestral-25-08` | 256K | + +## Features + +- Text generation, streaming, tool calling, RAG + +## Sample + +See the [mistral sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/mistral). diff --git a/docs/src/content/docs/plugins/ollama.md b/docs/src/content/docs/plugins/ollama.md new file mode 100644 index 000000000..483ad9bc1 --- /dev/null +++ b/docs/src/content/docs/plugins/ollama.md @@ -0,0 +1,68 @@ +--- +title: Ollama +description: Run local models without API keys using Ollama. +--- + +The Ollama plugin enables local LLM inference without requiring API keys. + +## Installation + +```xml + + com.google.genkit + genkit-plugin-ollama + 1.0.0-SNAPSHOT + +``` + +## Prerequisites + +Install and run [Ollama](https://ollama.com/): + +```bash +# Install Ollama +curl -fsSL https://ollama.com/install.sh | sh + +# Pull a model +ollama pull gemma3n:e4b +``` + +## Configuration + +```bash +# Optional: configure Ollama host (default: http://localhost:11434) +export OLLAMA_HOST=http://localhost:11434 +``` + +## Usage + +```java +import com.google.genkit.plugins.ollama.OllamaPlugin; + +Genkit genkit = Genkit.builder() + .plugin(OllamaPlugin.create()) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("ollama/gemma3n") + .prompt("Tell me about AI") + .build()); +``` + +## Models + +Use any model available in Ollama. Popular choices: + +- `ollama/gemma3n` — Google Gemma 3n +- `ollama/llama3.1` — Meta Llama 3.1 +- `ollama/mistral` — Mistral 7B +- `ollama/codellama` — Code-focused model + +## Features + +- Text generation, streaming, local-first (no API key required) + +## Sample + +See the [ollama sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/ollama). diff --git a/docs/src/content/docs/plugins/openai.md b/docs/src/content/docs/plugins/openai.md new file mode 100644 index 000000000..3c82ecd17 --- /dev/null +++ b/docs/src/content/docs/plugins/openai.md @@ -0,0 +1,67 @@ +--- +title: OpenAI +description: Use OpenAI GPT models, DALL-E, and embeddings. +--- + +The OpenAI plugin provides access to OpenAI's models including GPT-4o, DALL-E, and text embeddings. + +## Installation + +```xml + + com.google.genkit + genkit-plugin-openai + 1.0.0-SNAPSHOT + +``` + +## Configuration + +```bash +export OPENAI_API_KEY=your-api-key +``` + +## Usage + +```java +import com.google.genkit.plugins.openai.OpenAIPlugin; + +Genkit genkit = Genkit.builder() + .plugin(OpenAIPlugin.create()) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("Tell me about AI") + .build()); +``` + +## Available models + +- `openai/gpt-4o` — Most capable model +- `openai/gpt-4o-mini` — Fast and cost-effective +- `openai/gpt-4-turbo` — Previous generation flagship +- `openai/o1-preview` — Reasoning model +- `openai/o1-mini` — Fast reasoning model + +## Embeddings + +```java +EmbedResponse response = genkit.embed( + "openai/text-embedding-3-small", documents +); +``` + +## Features + +- Text generation and streaming +- Vision (image understanding) +- Tool calling +- Embeddings (text-embedding-3-small, text-embedding-3-large) +- Image generation (DALL-E 3/2) +- RAG support + +## Sample + +See the [openai sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/openai). diff --git a/docs/src/content/docs/plugins/opentelemetry.md b/docs/src/content/docs/plugins/opentelemetry.md new file mode 100644 index 000000000..45a01cfc3 --- /dev/null +++ b/docs/src/content/docs/plugins/opentelemetry.md @@ -0,0 +1,151 @@ +--- +title: OpenTelemetry Plugin +description: Export Genkit traces and metrics to any OpenTelemetry-compatible backend. +--- + +Genkit Java has built-in OpenTelemetry support in its core module. All actions (models, tools, flows) are automatically instrumented with traces and metrics. You can export this telemetry data to any OpenTelemetry-compatible backend by registering custom span processors and meter providers. + +## Built-in instrumentation + +The core module automatically instruments every Genkit action with: + +- **Traces** — Rich span data with inputs, outputs, execution paths, session info, and error details. +- **Metrics** — Counters and histograms for request counts, latencies, token usage, and more. + +No additional dependencies are required for local development — the Dev UI displays traces automatically. + +## Dependencies + +The core Genkit module already includes the OpenTelemetry SDK. If you need to add custom exporters (e.g., OTLP, Jaeger, Zipkin), add the relevant exporter dependency: + +```xml + + + io.opentelemetry + opentelemetry-exporter-otlp + + + + + io.opentelemetry + opentelemetry-exporter-zipkin + +``` + +The OpenTelemetry BOM is managed by the Genkit parent POM, so you don't need to specify versions. + +## Registering a custom span processor + +Use `TelemetryConfig.registerSpanProcessor()` to add your own trace exporter. This works alongside the built-in Dev UI exporter — your spans are sent to both. + +```java +import com.google.genkit.core.telemetry.TelemetryConfig; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; + +// Create an OTLP exporter pointing to your collector +OtlpGrpcSpanExporter exporter = OtlpGrpcSpanExporter.builder() + .setEndpoint("http://localhost:4317") + .build(); + +// Wrap in a batch processor and register +BatchSpanProcessor processor = BatchSpanProcessor.builder(exporter).build(); +TelemetryConfig.registerSpanProcessor(processor); +``` + +You can register multiple span processors. Each one receives all Genkit trace spans. + +## Registering a custom meter provider + +Use `TelemetryConfig.setMeterProvider()` to route metrics to your backend: + +```java +import com.google.genkit.core.telemetry.TelemetryConfig; +import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; + +// Create metric exporter +OtlpGrpcMetricExporter metricExporter = OtlpGrpcMetricExporter.builder() + .setEndpoint("http://localhost:4317") + .build(); + +// Create meter provider with periodic reader +SdkMeterProvider meterProvider = SdkMeterProvider.builder() + .registerMetricReader( + PeriodicMetricReader.builder(metricExporter).build() + ) + .build(); + +TelemetryConfig.setMeterProvider(meterProvider); +``` + +Once set, all Genkit metrics (`genkit/ai/generate/requests`, `genkit/ai/generate/latency`, token counters, etc.) are automatically exported. + +## Full example: OTLP collector + +Send all traces and metrics to an OpenTelemetry Collector running locally: + +```java +import com.google.genkit.Genkit; +import com.google.genkit.core.telemetry.TelemetryConfig; +import com.google.genkit.plugins.googlegenai.GoogleGenAIPlugin; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; +import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; + +public class App { + public static void main(String[] args) { + // Configure OTLP trace export + OtlpGrpcSpanExporter spanExporter = OtlpGrpcSpanExporter.builder() + .setEndpoint("http://localhost:4317") + .build(); + TelemetryConfig.registerSpanProcessor( + BatchSpanProcessor.builder(spanExporter).build() + ); + + // Configure OTLP metric export + OtlpGrpcMetricExporter metricExporter = OtlpGrpcMetricExporter.builder() + .setEndpoint("http://localhost:4317") + .build(); + TelemetryConfig.setMeterProvider( + SdkMeterProvider.builder() + .registerMetricReader( + PeriodicMetricReader.builder(metricExporter).build() + ) + .build() + ); + + // Initialize Genkit — telemetry is already wired + Genkit genkit = Genkit.builder() + .addPlugin(new GoogleGenAIPlugin()) + .build(); + + // All generate/flow/tool calls now export traces and metrics + genkit.generate(options -> options.model("googleai/gemini-2.0-flash").prompt("Hi")); + } +} +``` + +## Supported backends + +Because Genkit uses standard OpenTelemetry APIs, you can export to any compatible backend: + +| Backend | Exporter | +|---------|----------| +| [Jaeger](https://www.jaegertracing.io/) | `opentelemetry-exporter-otlp` | +| [Zipkin](https://zipkin.io/) | `opentelemetry-exporter-zipkin` | +| [Grafana / Tempo](https://grafana.com/oss/tempo/) | `opentelemetry-exporter-otlp` | +| [Datadog](https://www.datadoghq.com/) | OTLP via Datadog Agent | +| [New Relic](https://newrelic.com/) | `opentelemetry-exporter-otlp` | +| [Honeycomb](https://www.honeycomb.io/) | `opentelemetry-exporter-otlp` | +| [Google Cloud](https://cloud.google.com/) | Firebase plugin (built-in) | +| [AWS X-Ray](https://aws.amazon.com/xray/) | `opentelemetry-exporter-otlp` | + +For Google Cloud specifically, the [Firebase plugin](/genkit-java/plugins/firebase/) provides a turnkey integration. + +## Trace attributes reference + +See the [Observability overview](/genkit-java/observability/) for the full list of trace attributes and metrics emitted by Genkit. diff --git a/docs/src/content/docs/plugins/overview.mdx b/docs/src/content/docs/plugins/overview.mdx new file mode 100644 index 000000000..db8a3dcf1 --- /dev/null +++ b/docs/src/content/docs/plugins/overview.mdx @@ -0,0 +1,73 @@ +--- +title: Plugin Overview +description: Browse all available Genkit Java plugins. +--- + +import { LinkCard, CardGrid } from '@astrojs/starlight/components'; + +Genkit Java provides 20+ plugins organized into categories. + +## Model Plugins + +Connect to AI model providers through a unified API. + + + + + + + + + + + + + + + + +## Server & Deployment + + + + + + + +## Vector Databases + + + + + + + + +## Other + + + + + + +## Installation + +Each plugin is a separate Maven dependency: + +```xml + + com.google.genkit + genkit-plugin-{name} + 1.0.0-SNAPSHOT + +``` + +For example, to install the OpenAI plugin: + +```xml + + com.google.genkit + genkit-plugin-openai + 1.0.0-SNAPSHOT + +``` diff --git a/docs/src/content/docs/plugins/pinecone.md b/docs/src/content/docs/plugins/pinecone.md new file mode 100644 index 000000000..253970c32 --- /dev/null +++ b/docs/src/content/docs/plugins/pinecone.md @@ -0,0 +1,42 @@ +--- +title: Pinecone +description: Managed vector database for RAG applications. +--- + +## Installation + +```xml + + com.google.genkit + genkit-plugin-pinecone + 1.0.0-SNAPSHOT + +``` + +## Configuration + +```bash +export PINECONE_API_KEY=your-api-key +``` + +## Usage + +```java +import com.google.genkit.plugins.pinecone.PineconePlugin; + +Genkit genkit = Genkit.builder() + .plugin(PineconePlugin.create()) + .build(); +``` + +## Features + +- Serverless and Pod-based indexes +- Namespace support +- Metadata filtering +- Batch operations +- Similarity search + +## Sample + +See the [pinecone sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/pinecone). diff --git a/docs/src/content/docs/plugins/postgresql.md b/docs/src/content/docs/plugins/postgresql.md new file mode 100644 index 000000000..472d392ba --- /dev/null +++ b/docs/src/content/docs/plugins/postgresql.md @@ -0,0 +1,43 @@ +--- +title: PostgreSQL (pgvector) +description: Vector similarity search with PostgreSQL and pgvector extension. +--- + +## Installation + +```xml + + com.google.genkit + genkit-plugin-postgresql + 1.0.0-SNAPSHOT + +``` + +## Requirements + +- PostgreSQL 12+ with the [pgvector](https://github.com/pgvector/pgvector) extension installed +- Java 21+ + +## Usage + +```java +import com.google.genkit.plugins.postgresql.PostgreSQLPlugin; + +Genkit genkit = Genkit.builder() + .plugin(PostgreSQLPlugin.create( + "jdbc:postgresql://localhost:5432/mydb", + "user", "password")) + .build(); +``` + +## Features + +- Cosine, L2, and inner product similarity search +- Automatic schema management +- Connection pooling +- Batch indexing +- Metadata support + +## Sample + +See the [postgresql sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/postgresql). diff --git a/docs/src/content/docs/plugins/spring.md b/docs/src/content/docs/plugins/spring.md new file mode 100644 index 000000000..740ebccb6 --- /dev/null +++ b/docs/src/content/docs/plugins/spring.md @@ -0,0 +1,60 @@ +--- +title: Spring Boot +description: Expose Genkit flows as REST endpoints with Spring Boot. +--- + +## Installation + +```xml + + com.google.genkit + genkit-plugin-spring + 1.0.0-SNAPSHOT + +``` + +## Usage + +```java +import com.google.genkit.plugins.spring.SpringPlugin; + +SpringPlugin spring = SpringPlugin.create(); + +Genkit genkit = Genkit.builder() + .plugin(spring) + .build(); + +// Define your flows here... + +// Start the server (blocks until stopped) +spring.start(); +``` + +:::caution +You must call `spring.start()` after building the `Genkit` instance and defining your flows. Without it, the Spring Boot server will not start and your endpoints won't be accessible. +::: + +## Endpoints + +| Endpoint | Description | +|----------|-------------| +| `GET /health` | Health check | +| `GET /api/flows` | List all registered flows | +| `POST /api/flows/{flowName}` | Execute a flow | + +## Configuration + +| Option | Default | Description | +|--------|---------|-------------| +| `port` | 8080 | Server port | +| `host` | 0.0.0.0 | Bind address | +| `basePath` | `/api` | Base path for endpoints | +| `contextPath` | `/` | Application context path | + +## Features + +Full Spring Boot ecosystem integration, including dependency injection, configuration, and middleware. + +## Sample + +See the [spring sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/spring). diff --git a/docs/src/content/docs/plugins/weaviate.md b/docs/src/content/docs/plugins/weaviate.md new file mode 100644 index 000000000..ccac01e05 --- /dev/null +++ b/docs/src/content/docs/plugins/weaviate.md @@ -0,0 +1,47 @@ +--- +title: Weaviate +description: Vector similarity search with Weaviate for RAG applications. +--- + +## Installation + +```xml + + com.google.genkit + genkit-plugin-weaviate + 1.0.0-SNAPSHOT + +``` + +## Usage + +### Local Docker deployment + +```java +import com.google.genkit.plugins.weaviate.WeaviatePlugin; + +Genkit genkit = Genkit.builder() + .plugin(WeaviatePlugin.createLocal("http://localhost:8080")) + .build(); +``` + +### Weaviate Cloud + +```java +Genkit genkit = Genkit.builder() + .plugin(WeaviatePlugin.createCloud( + "https://your-cluster.weaviate.network", + "your-api-key")) + .build(); +``` + +## Features + +- Configurable distance measures (COSINE, L2_SQUARED, DOT) +- Batch indexing +- Automatic collection creation +- Flexible retrieval + +## Sample + +See the [weaviate sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/weaviate). diff --git a/docs/src/content/docs/plugins/xai.md b/docs/src/content/docs/plugins/xai.md new file mode 100644 index 000000000..440202d3d --- /dev/null +++ b/docs/src/content/docs/plugins/xai.md @@ -0,0 +1,55 @@ +--- +title: xAI (Grok) +description: Use xAI Grok models for text generation. +--- + +The xAI plugin provides access to Grok models. + +## Installation + +```xml + + com.google.genkit + genkit-plugin-xai + 1.0.0-SNAPSHOT + +``` + +## Configuration + +```bash +export XAI_API_KEY=your-api-key +``` + +## Usage + +```java +import com.google.genkit.plugins.xai.XAIPlugin; + +Genkit genkit = Genkit.builder() + .plugin(XAIPlugin.create()) + .build(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("xai/grok-3") + .prompt("Tell me about AI") + .build()); +``` + +## Available models + +| Model | Context Window | +|-------|---------------| +| `xai/grok-4` | Up to 2M tokens | +| `xai/grok-4-1-fast` | 131K tokens | +| `xai/grok-3` | 131K tokens | +| `xai/grok-3-mini` | 131K tokens | + +## Features + +- Text generation, streaming, tool calling, RAG + +## Sample + +See the [xai sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/xai). diff --git a/docs/src/content/docs/rag.mdx b/docs/src/content/docs/rag.mdx new file mode 100644 index 000000000..d32ae5897 --- /dev/null +++ b/docs/src/content/docs/rag.mdx @@ -0,0 +1,77 @@ +--- +title: RAG (Retrieval Augmented Generation) +description: Build RAG applications with retrievers, indexers, and vector stores. +--- + +RAG enhances AI model output by injecting relevant information from your data into prompts before sending them to the model. + +## Defining a retriever + +```java +Retriever myRetriever = genkit.defineRetriever("myStore/docs", (ctx, request) -> { + List docs = findSimilarDocs(request.getQuery()); + return new RetrieverResponse(docs); +}); +``` + +## Defining an indexer + +```java +Indexer myIndexer = genkit.defineIndexer("myStore/docs", (ctx, request) -> { + indexDocuments(request.getDocuments()); + return new IndexerResponse(); +}); +``` + +## Indexing documents + +```java +List docs = List.of( + Document.fromText("Paris is the capital of France."), + Document.fromText("Berlin is the capital of Germany.") +); +genkit.index("myStore/docs", docs); +``` + +## Retrieve and generate + +```java +// Retrieve relevant documents +List relevantDocs = genkit.retrieve( + "myStore/docs", "What is the capital of France?" +); + +// Generate with context +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("Answer based on context: What is the capital of France?") + .docs(relevantDocs) + .build()); +``` + +## Embeddings + +Generate vector embeddings for semantic search: + +```java +List documents = List.of( + Document.fromText("Hello world"), + Document.fromText("Goodbye world") +); +EmbedResponse response = genkit.embed("openai/text-embedding-3-small", documents); +``` + +## Vector store plugins + +Genkit Java supports multiple vector databases out of the box: + +import { LinkCard, CardGrid } from '@astrojs/starlight/components'; + + + + + + + + diff --git a/docs/src/content/docs/samples.md b/docs/src/content/docs/samples.md new file mode 100644 index 000000000..e17c86dce --- /dev/null +++ b/docs/src/content/docs/samples.md @@ -0,0 +1,65 @@ +--- +title: Samples +description: Example applications for every supported provider and feature. +--- + +All samples are located in the [`samples/`](https://github.com/genkit-ai/genkit-java/tree/main/samples) directory. + +## Running a sample + +```bash +# Set your API key +export OPENAI_API_KEY=your-api-key +# Or: export GOOGLE_GENAI_API_KEY=your-api-key + +# Navigate to a sample and run +cd samples/openai +./run.sh + +# Or with Genkit Dev UI (recommended) +genkit start -- ./run.sh +``` + +## Available samples + +### Model provider samples + +| Sample | Description | +|--------|-------------| +| **openai** | Basic OpenAI integration with flows and tools | +| **google-genai** | Google Gemini integration with image generation | +| **anthropic** | Anthropic Claude integration with streaming | +| **aws-bedrock** | AWS Bedrock models integration | +| **azure-foundry** | Azure AI Foundry models integration | +| **xai** | xAI Grok models integration | +| **deepseek** | DeepSeek models integration | +| **cohere** | Cohere Command models integration | +| **mistral** | Mistral AI models integration | +| **groq** | Groq ultra-fast inference integration | +| **ollama** | Local Ollama models with Gemma 3n | + +### Feature samples + +| Sample | Description | +|--------|-------------| +| **dotprompt** | DotPrompt files with complex inputs/outputs, variants, and partials | +| **structured-output** | Type-safe structured output generation | +| **rag** | RAG application with local vector store | +| **chat-session** | Multi-turn chat with session persistence | +| **evaluations** | Custom evaluators and evaluation workflows | +| **evaluators-plugin** | Pre-built RAGAS-style evaluators plugin demo | +| **complex-io** | Complex nested types, arrays, maps in flow inputs/outputs | +| **middleware** | Middleware patterns for logging, caching, rate limiting | +| **multi-agent** | Multi-agent orchestration patterns | +| **interrupts** | Flow interrupts and human-in-the-loop patterns | +| **mcp** | Model Context Protocol (MCP) integration | + +### Deployment & infrastructure samples + +| Sample | Description | +|--------|-------------| +| **firebase** | Firebase integration with Firestore RAG and Cloud Functions | +| **spring** | Spring Boot HTTP server integration | +| **weaviate** | Weaviate vector database RAG | +| **postgresql** | PostgreSQL pgvector RAG | +| **pinecone** | Pinecone vector database RAG | diff --git a/docs/src/content/docs/streaming.md b/docs/src/content/docs/streaming.md new file mode 100644 index 000000000..5986aeb54 --- /dev/null +++ b/docs/src/content/docs/streaming.md @@ -0,0 +1,35 @@ +--- +title: Streaming +description: Stream AI responses in real-time for better user experience. +--- + +Streaming lets you present output as it's generated, improving perceived responsiveness. + +## Basic streaming + +```java +StringBuilder result = new StringBuilder(); +ModelResponse response = genkit.generateStream( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("Tell me a story") + .build(), + chunk -> { + // Process each chunk as it arrives + System.out.print(chunk.getText()); + result.append(chunk.getText()); + }); +``` + +## How it works + +1. Call `generateStream()` instead of `generate()` +2. Provide a callback that receives each chunk +3. Each chunk contains partial text as it's generated +4. The method returns the complete `ModelResponse` when done + +## Use cases + +- **Chat interfaces** — Display responses as they're typed +- **Long-form content** — Show progress during generation +- **Real-time applications** — Reduce time to first token diff --git a/docs/src/content/docs/structured-output.md b/docs/src/content/docs/structured-output.md new file mode 100644 index 000000000..c55f9b514 --- /dev/null +++ b/docs/src/content/docs/structured-output.md @@ -0,0 +1,88 @@ +--- +title: Structured Output +description: Generate type-safe outputs with automatic JSON schema generation. +--- + +Genkit supports generating structured, type-safe outputs using Jackson annotations. This ensures the model's response conforms to your Java types. + +## Defining an output class + +Use Jackson annotations to define your output schema: + +```java +public class MenuItem { + @JsonProperty(required = true) + @JsonPropertyDescription("The name of the menu item") + private String name; + + @JsonProperty(required = true) + @JsonPropertyDescription("A detailed description") + private String description; + + @JsonProperty(required = true) + @JsonPropertyDescription("Price in dollars") + private double price; + + @JsonPropertyDescription("Preparation time in minutes") + private int prepTimeMinutes; + + @JsonPropertyDescription("Dietary information (e.g., vegan, gluten-free)") + private List dietaryInfo; + + // getters/setters... +} +``` + +## Generating structured output + +```java +MenuItem item = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("Suggest a fancy French menu item") + .outputClass(MenuItem.class) + .build() +); + +System.out.println(item.getName()); // "Coq au Vin" +System.out.println(item.getPrice()); // 28.50 +System.out.println(item.getDietaryInfo()); // ["gluten-free"] +``` + +## In flows + +```java +genkit.defineFlow( + "generateMenuItem", + MenuItemRequest.class, + MenuItem.class, + (ctx, request) -> genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt(request.getDescription()) + .outputClass(MenuItem.class) + .build() + ) +); +``` + +## With DotPrompt + +```java +ExecutablePrompt prompt = genkit.prompt("italian-dish", DishRequest.class); +MenuItem dish = prompt.generate(new DishRequest("Italian"), MenuItem.class); +``` + +## With tools + +```java +Tool recipeGen = genkit.defineTool( + "generateRecipe", + "Generates a recipe", + (ctx, request) -> new MenuItem(...), + RecipeRequest.class, + MenuItem.class +); +``` + +See the [structured-output sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/structured-output) for complete examples. diff --git a/docs/src/content/docs/tools.mdx b/docs/src/content/docs/tools.mdx new file mode 100644 index 000000000..081eecec3 --- /dev/null +++ b/docs/src/content/docs/tools.mdx @@ -0,0 +1,81 @@ +--- +title: Tool Calling +description: Define tools that AI models can call during generation. +--- + +Tools extend the capabilities of AI models by giving them the ability to call your functions. Models trained for tool calling can respond with a request to invoke a tool, and Genkit handles the execution automatically. + +## Defining a tool + +```java +@SuppressWarnings("unchecked") +Tool, Map> weatherTool = genkit.defineTool( + "getWeather", + "Gets the current weather for a location", + Map.of( + "type", "object", + "properties", Map.of( + "location", Map.of( + "type", "string", + "description", "The city name" + ) + ), + "required", new String[]{"location"} + ), + (Class>) (Class) Map.class, + (ctx, input) -> { + String location = (String) input.get("location"); + return Map.of( + "location", location, + "temperature", "72°F", + "conditions", "sunny" + ); + }); +``` + +## Using tools in generation + +Pass tools to `generate()` — the tool execution loop is handled automatically: + +```java +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("What's the weather in Paris?") + .tools(List.of(weatherTool)) + .build()); + +System.out.println(response.getText()); +// Output: "The weather in Paris is currently 72°F and sunny." +``` + +## How it works + +1. You define tools with a name, description, and input schema +2. The model receives the tool definitions alongside your prompt +3. If the model decides to call a tool, Genkit automatically executes it +4. The tool result is sent back to the model for final response generation + +## Typed tools + +You can also define tools with typed input/output classes: + +```java +Tool recipeGen = genkit.defineTool( + "generateRecipe", + "Generates a recipe", + (ctx, request) -> new MenuItem(...), + RecipeRequest.class, + MenuItem.class +); +``` + +## Next steps + +import { LinkCard, CardGrid } from '@astrojs/starlight/components'; + + + + + + diff --git a/docs/src/styles/custom.css b/docs/src/styles/custom.css new file mode 100644 index 000000000..6b75c876f --- /dev/null +++ b/docs/src/styles/custom.css @@ -0,0 +1,60 @@ +:root { + --sl-color-accent-low: #1e1b4b; + --sl-color-accent: #7c3aed; + --sl-color-accent-high: #ddd6fe; + --sl-color-white: #ffffff; + --sl-color-gray-1: #eceef2; + --sl-color-gray-2: #c0c2c7; + --sl-color-gray-3: #888b96; + --sl-color-gray-4: #545861; + --sl-color-gray-5: #353841; + --sl-color-gray-6: #24272f; + --sl-color-black: #17181c; +} + +:root[data-theme="light"] { + --sl-color-accent-low: #ede9fe; + --sl-color-accent: #7c3aed; + --sl-color-accent-high: #1e1b4b; + --sl-color-white: #17181c; + --sl-color-gray-1: #24272f; + --sl-color-gray-2: #353841; + --sl-color-gray-3: #545861; + --sl-color-gray-4: #888b96; + --sl-color-gray-5: #c0c2c7; + --sl-color-gray-6: #eceef2; + --sl-color-black: #ffffff; +} + +/* Hero section styling */ +.hero { + padding-block: 2rem; +} + +/* Badge styling */ +.sl-badge { + font-size: 0.75rem; +} + +/* Move search bar to the right, next to social icons (like genkit.dev) */ +@media (min-width: 50rem) { + .header { + /* Title takes free space, pushing icons + search to the right */ + grid-template-columns: 1fr auto auto !important; + } + + /* Place search before social icons, right-group last */ + .header > div:nth-child(2) { + order: 2; + justify-self: end; + } + + .header > .right-group { + order: 3; + } +} + +/* Hide search on the splash/landing page (no sidebar) */ +html:not([data-has-sidebar]) .header > div:nth-child(2) { + display: none; +} diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 000000000..bcbf8b509 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "astro/tsconfigs/strict" +} From 6f3572f4c48c95a6565f37a46d1b8569367f954d Mon Sep 17 00:00:00 2001 From: xavidop Date: Mon, 30 Mar 2026 17:31:45 +0200 Subject: [PATCH 18/50] fix: build --- .gitignore | 1 - docs/package-lock.json | 7343 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 7343 insertions(+), 1 deletion(-) create mode 100644 docs/package-lock.json diff --git a/.gitignore b/.gitignore index 35a6456fe..5c2d3f4f6 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ dist lib node_modules -package-lock.json # Java /**/target/ /**/*.class diff --git a/docs/package-lock.json b/docs/package-lock.json new file mode 100644 index 000000000..420a3f934 --- /dev/null +++ b/docs/package-lock.json @@ -0,0 +1,7343 @@ +{ + "name": "genkit-java-docs", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "genkit-java-docs", + "version": "0.0.1", + "dependencies": { + "@astrojs/starlight": "^0.34.0", + "astro": "5.17.3", + "sharp": "^0.33.0" + } + }, + "node_modules/@astrojs/compiler": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.13.1.tgz", + "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==", + "license": "MIT" + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.6.tgz", + "integrity": "sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q==", + "license": "MIT" + }, + "node_modules/@astrojs/markdown-remark": { + "version": "6.3.11", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.11.tgz", + "integrity": "sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.7.6", + "@astrojs/prism": "3.3.0", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "import-meta-resolve": "^4.2.0", + "js-yaml": "^4.1.1", + "mdast-util-definitions": "^6.0.0", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", + "remark-smartypants": "^3.0.2", + "shiki": "^3.21.0", + "smol-toml": "^1.6.0", + "unified": "^11.0.5", + "unist-util-remove-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.2", + "vfile": "^6.0.3" + } + }, + "node_modules/@astrojs/mdx": { + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-4.3.14.tgz", + "integrity": "sha512-FBrqJQORVm+rkRa2TS5CjU9PBA6hkhrwLVBSS9A77gN2+iehvjq1w6yya/d0YKC7osiVorKkr3Qd9wNbl0ZkGA==", + "license": "MIT", + "dependencies": { + "@astrojs/markdown-remark": "6.3.11", + "@mdx-js/mdx": "^3.1.1", + "acorn": "^8.15.0", + "es-module-lexer": "^1.7.0", + "estree-util-visit": "^2.0.0", + "hast-util-to-html": "^9.0.5", + "piccolore": "^0.1.3", + "rehype-raw": "^7.0.0", + "remark-gfm": "^4.0.1", + "remark-smartypants": "^3.0.2", + "source-map": "^0.7.6", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.3" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + }, + "peerDependencies": { + "astro": "^5.0.0" + } + }, + "node_modules/@astrojs/prism": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz", + "integrity": "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@astrojs/sitemap": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.6.1.tgz", + "integrity": "sha512-+o+TbxXqQJAOd+HxCjz/5RdAMrRFGjeuO+U6zddUuTO59WqMqXnsc8uveRiEr2Ff+3McZiEne7iG4J5cnuI6kA==", + "license": "MIT", + "dependencies": { + "sitemap": "^8.0.2", + "stream-replace-string": "^2.0.0", + "zod": "^3.25.76" + } + }, + "node_modules/@astrojs/starlight": { + "version": "0.34.8", + "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.34.8.tgz", + "integrity": "sha512-XuYz0TfCZhje2u1Q9FNtmTdm7/B9QP91RDI1VkPgYvDhSYlME3k8gwgcBMHnR9ASDo2p9gskrqe7t1Pub/qryg==", + "license": "MIT", + "dependencies": { + "@astrojs/markdown-remark": "^6.3.1", + "@astrojs/mdx": "^4.2.3", + "@astrojs/sitemap": "^3.3.0", + "@pagefind/default-ui": "^1.3.0", + "@types/hast": "^3.0.4", + "@types/js-yaml": "^4.0.9", + "@types/mdast": "^4.0.4", + "astro-expressive-code": "^0.41.1", + "bcp-47": "^2.1.0", + "hast-util-from-html": "^2.0.1", + "hast-util-select": "^6.0.2", + "hast-util-to-string": "^3.0.0", + "hastscript": "^9.0.0", + "i18next": "^23.11.5", + "js-yaml": "^4.1.0", + "klona": "^2.0.6", + "mdast-util-directive": "^3.0.0", + "mdast-util-to-markdown": "^2.1.0", + "mdast-util-to-string": "^4.0.0", + "pagefind": "^1.3.0", + "rehype": "^13.0.1", + "rehype-format": "^5.0.0", + "remark-directive": "^3.0.0", + "ultrahtml": "^1.6.0", + "unified": "^11.0.5", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.2" + }, + "peerDependencies": { + "astro": "^5.5.0" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz", + "integrity": "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==", + "license": "MIT", + "dependencies": { + "ci-info": "^4.2.0", + "debug": "^4.4.0", + "dlv": "^1.1.3", + "dset": "^3.1.4", + "is-docker": "^3.0.0", + "is-wsl": "^3.1.0", + "which-pm-runs": "^1.1.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@capsizecss/unpack": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.0.tgz", + "integrity": "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz", + "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@expressive-code/core": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.41.7.tgz", + "integrity": "sha512-ck92uZYZ9Wba2zxkiZLsZGi9N54pMSAVdrI9uW3Oo9AtLglD5RmrdTwbYPCT2S/jC36JGB2i+pnQtBm/Ib2+dg==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^4.0.4", + "hast-util-select": "^6.0.2", + "hast-util-to-html": "^9.0.1", + "hast-util-to-text": "^4.0.1", + "hastscript": "^9.0.0", + "postcss": "^8.4.38", + "postcss-nested": "^6.0.1", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.1" + } + }, + "node_modules/@expressive-code/plugin-frames": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.41.7.tgz", + "integrity": "sha512-diKtxjQw/979cTglRFaMCY/sR6hWF0kSMg8jsKLXaZBSfGS0I/Hoe7Qds3vVEgeoW+GHHQzMcwvgx/MOIXhrTA==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.41.7" + } + }, + "node_modules/@expressive-code/plugin-shiki": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.41.7.tgz", + "integrity": "sha512-DL605bLrUOgqTdZ0Ot5MlTaWzppRkzzqzeGEu7ODnHF39IkEBbFdsC7pbl3LbUQ1DFtnfx6rD54k/cdofbW6KQ==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.41.7", + "shiki": "^3.2.2" + } + }, + "node_modules/@expressive-code/plugin-text-markers": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.41.7.tgz", + "integrity": "sha512-Ewpwuc5t6eFdZmWlFyeuy3e1PTQC0jFvw2Q+2bpcWXbOZhPLsT7+h8lsSIJxb5mS7wZko7cKyQ2RLYDyK6Fpmw==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.41.7" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@mdx-js/mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", + "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdx": "^2.0.0", + "acorn": "^8.0.0", + "collapse-white-space": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-util-scope": "^1.0.0", + "estree-walker": "^3.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "markdown-extensions": "^2.0.0", + "recma-build-jsx": "^1.0.0", + "recma-jsx": "^1.0.0", + "recma-stringify": "^1.0.0", + "rehype-recma": "^1.0.0", + "remark-mdx": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "source-map": "^0.7.0", + "unified": "^11.0.0", + "unist-util-position-from-estree": "^2.0.0", + "unist-util-stringify-position": "^4.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", + "license": "MIT" + }, + "node_modules/@pagefind/darwin-arm64": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.4.0.tgz", + "integrity": "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/darwin-x64": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.4.0.tgz", + "integrity": "sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@pagefind/default-ui": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.4.0.tgz", + "integrity": "sha512-wie82VWn3cnGEdIjh4YwNESyS1G6vRHwL6cNjy9CFgNnWW/PGRjsLq300xjVH5sfPFK3iK36UxvIBymtQIEiSQ==", + "license": "MIT" + }, + "node_modules/@pagefind/freebsd-x64": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.4.0.tgz", + "integrity": "sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@pagefind/linux-arm64": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.4.0.tgz", + "integrity": "sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/linux-x64": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.4.0.tgz", + "integrity": "sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@pagefind/windows-x64": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.4.0.tgz", + "integrity": "sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz", + "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz", + "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.4" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", + "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", + "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", + "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/types": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", + "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-iterate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", + "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, + "node_modules/astro": { + "version": "5.17.3", + "resolved": "https://registry.npmjs.org/astro/-/astro-5.17.3.tgz", + "integrity": "sha512-69dcfPe8LsHzklwj+hl+vunWUbpMB6pmg35mACjetxbJeUNNys90JaBM8ZiwsPK689SAj/4Zqb1ayaANls9/MA==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler": "^2.13.0", + "@astrojs/internal-helpers": "0.7.5", + "@astrojs/markdown-remark": "6.3.10", + "@astrojs/telemetry": "3.3.0", + "@capsizecss/unpack": "^4.0.0", + "@oslojs/encoding": "^1.1.0", + "@rollup/pluginutils": "^5.3.0", + "acorn": "^8.15.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "boxen": "8.0.1", + "ci-info": "^4.3.1", + "clsx": "^2.1.1", + "common-ancestor-path": "^1.0.1", + "cookie": "^1.1.1", + "cssesc": "^3.0.0", + "debug": "^4.4.3", + "deterministic-object-hash": "^2.0.2", + "devalue": "^5.6.2", + "diff": "^8.0.3", + "dlv": "^1.1.3", + "dset": "^3.1.4", + "es-module-lexer": "^1.7.0", + "esbuild": "^0.27.3", + "estree-walker": "^3.0.3", + "flattie": "^1.1.1", + "fontace": "~0.4.0", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.2.0", + "import-meta-resolve": "^4.2.0", + "js-yaml": "^4.1.1", + "magic-string": "^0.30.21", + "magicast": "^0.5.1", + "mrmime": "^2.0.1", + "neotraverse": "^0.6.18", + "p-limit": "^6.2.0", + "p-queue": "^8.1.1", + "package-manager-detector": "^1.6.0", + "piccolore": "^0.1.3", + "picomatch": "^4.0.3", + "prompts": "^2.4.2", + "rehype": "^13.0.2", + "semver": "^7.7.3", + "shiki": "^3.21.0", + "smol-toml": "^1.6.0", + "svgo": "^4.0.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tsconfck": "^3.1.6", + "ultrahtml": "^1.6.0", + "unifont": "~0.7.3", + "unist-util-visit": "^5.0.0", + "unstorage": "^1.17.4", + "vfile": "^6.0.3", + "vite": "^6.4.1", + "vitefu": "^1.1.1", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^21.1.1", + "yocto-spinner": "^0.2.3", + "zod": "^3.25.76", + "zod-to-json-schema": "^3.25.1", + "zod-to-ts": "^1.2.0" + }, + "bin": { + "astro": "astro.js" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/astrodotbuild" + }, + "optionalDependencies": { + "sharp": "^0.34.0" + } + }, + "node_modules/astro-expressive-code": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.41.7.tgz", + "integrity": "sha512-hUpogGc6DdAd+I7pPXsctyYPRBJDK7Q7d06s4cyP0Vz3OcbziP3FNzN0jZci1BpCvLn9675DvS7B9ctKKX64JQ==", + "license": "MIT", + "dependencies": { + "rehype-expressive-code": "^0.41.7" + }, + "peerDependencies": { + "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta" + } + }, + "node_modules/astro/node_modules/@astrojs/internal-helpers": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.5.tgz", + "integrity": "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA==", + "license": "MIT" + }, + "node_modules/astro/node_modules/@astrojs/markdown-remark": { + "version": "6.3.10", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.10.tgz", + "integrity": "sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.7.5", + "@astrojs/prism": "3.3.0", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "hast-util-to-text": "^4.0.2", + "import-meta-resolve": "^4.2.0", + "js-yaml": "^4.1.1", + "mdast-util-definitions": "^6.0.0", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", + "remark-smartypants": "^3.0.2", + "shiki": "^3.19.0", + "smol-toml": "^1.5.2", + "unified": "^11.0.5", + "unist-util-remove-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "unist-util-visit-parents": "^6.0.2", + "vfile": "^6.0.3" + } + }, + "node_modules/astro/node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/astro/node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/astro/node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/astro/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/astro/node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/astro/node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/astro/node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/astro/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/astro/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/astro/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/astro/node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/astro/node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/astro/node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/astro/node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/astro/node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/astro/node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/astro/node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/astro/node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/astro/node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/astro/node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/base-64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", + "license": "MIT" + }, + "node_modules/bcp-47": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-2.1.0.tgz", + "integrity": "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/bcp-47-match": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", + "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/boxen": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", + "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^8.0.0", + "chalk": "^5.3.0", + "cli-boxes": "^3.0.0", + "string-width": "^7.2.0", + "type-fest": "^4.21.0", + "widest-line": "^5.0.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/common-ancestor-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", + "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", + "license": "ISC" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cookie-es": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", + "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", + "license": "MIT" + }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-selector-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.3.0.tgz", + "integrity": "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/deterministic-object-hash": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz", + "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==", + "license": "MIT", + "dependencies": { + "base-64": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/devalue": { + "version": "5.6.4", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.4.tgz", + "integrity": "sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/direction": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz", + "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==", + "license": "MIT", + "bin": { + "direction": "cli.js" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "license": "MIT" + }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/expressive-code": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.41.7.tgz", + "integrity": "sha512-2wZjC8OQ3TaVEMcBtYY4Va3lo6J+Ai9jf3d4dbhURMJcU4Pbqe6EcHe424MIZI0VHUA1bR6xdpoHYi3yxokWqA==", + "license": "MIT", + "dependencies": { + "@expressive-code/core": "^0.41.7", + "@expressive-code/plugin-frames": "^0.41.7", + "@expressive-code/plugin-shiki": "^0.41.7", + "@expressive-code/plugin-text-markers": "^0.41.7" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fontace": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz", + "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.2" + } + }, + "node_modules/fontkitten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz", + "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==", + "license": "MIT", + "dependencies": { + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, + "node_modules/h3": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.10.tgz", + "integrity": "sha512-YzJeWSkDZxAhvmp8dexjRK5hxziRO7I9m0N53WhvYL5NiWfkUkzssVzY9jvGu0HBoLFW6+duYmNSn6MaZBCCtg==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.2", + "crossws": "^0.3.5", + "defu": "^6.1.4", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, + "node_modules/hast-util-embedded": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", + "integrity": "sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-format": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hast-util-format/-/hast-util-format-1.1.0.tgz", + "integrity": "sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-minify-whitespace": "^1.0.0", + "hast-util-phrasing": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "html-whitespace-sensitive-tag-names": "^3.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-has-property": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-body-ok-link": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-is-body-ok-link/-/hast-util-is-body-ok-link-3.0.1.tgz", + "integrity": "sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-minify-whitespace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hast-util-minify-whitespace/-/hast-util-minify-whitespace-1.0.1.tgz", + "integrity": "sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-phrasing/-/hast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-embedded": "^3.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-is-body-ok-link": "^3.0.0", + "hast-util-is-element": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-select": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz", + "integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "bcp-47-match": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "css-selector-parser": "^3.0.0", + "devlop": "^1.0.0", + "direction": "^2.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "nth-check": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-estree": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", + "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-attach-comments": "^3.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-string": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-whitespace-sensitive-tag-names": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-whitespace-sensitive-tag-names/-/html-whitespace-sensitive-tag-names-3.0.1.tgz", + "integrity": "sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/i18next": { + "version": "23.16.8", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz", + "integrity": "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "license": "MIT" + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", + "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-definitions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", + "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-directive": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz", + "integrity": "sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", + "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", + "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", + "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "micromark-factory-mdx-expression": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "license": "MIT", + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-extension-mdxjs-esm": "^3.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", + "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-events-to-acorn": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-position-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", + "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "estree-util-visit": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "vfile-message": "^4.0.0" + } + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neotraverse": { + "version": "0.6.18", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", + "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-mock-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", + "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, + "node_modules/oniguruma-parser": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", + "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.5.tgz", + "integrity": "sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.1", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/p-limit": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", + "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz", + "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", + "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", + "license": "MIT" + }, + "node_modules/pagefind": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.4.0.tgz", + "integrity": "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g==", + "license": "MIT", + "bin": { + "pagefind": "lib/runner/bin.cjs" + }, + "optionalDependencies": { + "@pagefind/darwin-arm64": "1.4.0", + "@pagefind/darwin-x64": "1.4.0", + "@pagefind/freebsd-x64": "1.4.0", + "@pagefind/linux-arm64": "1.4.0", + "@pagefind/linux-x64": "1.4.0", + "@pagefind/windows-x64": "1.4.0" + } + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/parse-latin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", + "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "@types/unist": "^3.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-modify-children": "^4.0.0", + "unist-util-visit-children": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/piccolore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", + "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", + "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", + "license": "MIT", + "dependencies": { + "acorn-jsx": "^5.0.0", + "estree-util-to-js": "^2.0.0", + "recma-parse": "^1.0.0", + "recma-stringify": "^1.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/rehype": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", + "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "rehype-parse": "^9.0.0", + "rehype-stringify": "^10.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-expressive-code": { + "version": "0.41.7", + "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.41.7.tgz", + "integrity": "sha512-25f8ZMSF1d9CMscX7Cft0TSQIqdwjce2gDOvQ+d/w0FovsMwrSt3ODP4P3Z7wO1jsIJ4eYyaDRnIR/27bd/EMQ==", + "license": "MIT", + "dependencies": { + "expressive-code": "^0.41.7" + } + }, + "node_modules/rehype-format": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/rehype-format/-/rehype-format-5.0.1.tgz", + "integrity": "sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-format": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-directive": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/remark-directive/-/remark-directive-3.0.1.tgz", + "integrity": "sha512-gwglrEQEZcZYgVyG1tQuA+h58EZfq5CSULw7J90AFuCTyib1thgHPoqQ+h9iFvU6R+vnZ5oNFQR5QKgGpk741A==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-directive": "^3.0.0", + "micromark-extension-directive": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", + "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-smartypants": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", + "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", + "license": "MIT", + "dependencies": { + "retext": "^9.0.0", + "retext-smartypants": "^6.0.0", + "unified": "^11.0.4", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", + "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "retext-latin": "^4.0.0", + "retext-stringify": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-latin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", + "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "parse-latin": "^7.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/retext-stringify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", + "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/shiki": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz", + "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "3.23.0", + "@shikijs/engine-javascript": "3.23.0", + "@shikijs/engine-oniguruma": "3.23.0", + "@shikijs/langs": "3.23.0", + "@shikijs/themes": "3.23.0", + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/sitemap": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-8.0.3.tgz", + "integrity": "sha512-9Ew1tR2WYw8RGE2XLy7GjkusvYXy8Rg6y8TYuBuQMfIEdGcWoJpY2Wr5DzsEiL/TKCw56+YKTCCUHglorEYK+A==", + "license": "MIT", + "dependencies": { + "@types/node": "^17.0.5", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/sitemap/node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "license": "MIT" + }, + "node_modules/smol-toml": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz", + "integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stream-replace-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", + "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/svgo": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz", + "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tsconfck": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", + "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", + "license": "MIT", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "license": "MIT" + }, + "node_modules/ultrahtml": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.6.0.tgz", + "integrity": "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==", + "license": "MIT" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unifont": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.4.tgz", + "integrity": "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.1.0", + "ofetch": "^1.5.1", + "ohash": "^2.0.11" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-modify-children": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", + "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "array-iterate": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-children": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", + "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/vitefu": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.2.tgz", + "integrity": "sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw==", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/which-pm-runs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", + "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/widest-line": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", + "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", + "license": "MIT", + "dependencies": { + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", + "license": "MIT" + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yocto-spinner": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.2.3.tgz", + "integrity": "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==", + "license": "MIT", + "dependencies": { + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": ">=18.19" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, + "node_modules/zod-to-ts": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz", + "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==", + "peerDependencies": { + "typescript": "^4.9.4 || ^5.0.2", + "zod": "^3" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} From 3ecd7c5f909198cfae50572742e1d95633b38608 Mon Sep 17 00:00:00 2001 From: xavidop Date: Mon, 30 Mar 2026 17:39:04 +0200 Subject: [PATCH 19/50] fix: more docs fixes --- README.md | 2 + docs/astro.config.mjs | 2 +- docs/package-lock.json | 47 +++---------------- docs/package.json | 2 +- docs/src/content/docs/chat-sessions.md | 2 +- docs/src/content/docs/evaluations.md | 4 +- docs/src/content/docs/index.mdx | 2 +- docs/src/content/docs/interrupts.md | 2 +- docs/src/content/docs/middleware.md | 4 +- docs/src/content/docs/multi-agent.md | 2 +- .../docs/plugins/firebase-functions.md | 2 +- .../docs/plugins/firebase-vector-store.md | 2 +- docs/src/content/docs/plugins/firebase.md | 2 +- 13 files changed, 21 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 4b2cbaa8d..9247dfaa2 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Genkit for Java is the Java implementation of the Genkit framework for building See: https://genkit.dev +Check the Docs: https://genkit-ai.github.io/genkit-java + > **Status**: Currently in active development (1.0.0-SNAPSHOT). Requires Java 21+. > > **Note**: The Java SDK supports OpenAI, Google GenAI (Gemini), Anthropic (Claude), AWS Bedrock, Azure AI Foundry, XAI (Grok), DeepSeek, Cohere, Mistral, Groq, Ollama (local models), any OpenAI-compatible endpoint (via compat-oai), Firebase (Firestore vector search, Cloud Functions, telemetry), vector databases (Weaviate, PostgreSQL, Pinecone), MCP, and pre-built evaluators. See [Modules](#modules) for the full list. diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 3e3a93aeb..68be997e2 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -131,7 +131,7 @@ export default defineConfig({ items: [ { label: "Javadoc", - link: "/genkit-java/javadoc/", + link: "/javadoc/", attrs: { target: "_blank" }, }, ], diff --git a/docs/package-lock.json b/docs/package-lock.json index 420a3f934..7c3ac67b0 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "dependencies": { "@astrojs/starlight": "^0.34.0", - "astro": "5.17.3", + "astro": "^5.18.1", "sharp": "^0.33.0" } }, @@ -1986,14 +1986,14 @@ } }, "node_modules/astro": { - "version": "5.17.3", - "resolved": "https://registry.npmjs.org/astro/-/astro-5.17.3.tgz", - "integrity": "sha512-69dcfPe8LsHzklwj+hl+vunWUbpMB6pmg35mACjetxbJeUNNys90JaBM8ZiwsPK689SAj/4Zqb1ayaANls9/MA==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/astro/-/astro-5.18.1.tgz", + "integrity": "sha512-m4VWilWZ+Xt6NPoYzC4CgGZim/zQUO7WFL0RHCH0AiEavF1153iC3+me2atDvXpf/yX4PyGUeD8wZLq1cirT3g==", "license": "MIT", "dependencies": { "@astrojs/compiler": "^2.13.0", - "@astrojs/internal-helpers": "0.7.5", - "@astrojs/markdown-remark": "6.3.10", + "@astrojs/internal-helpers": "0.7.6", + "@astrojs/markdown-remark": "6.3.11", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^4.0.0", "@oslojs/encoding": "^1.1.0", @@ -2083,41 +2083,6 @@ "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta" } }, - "node_modules/astro/node_modules/@astrojs/internal-helpers": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.5.tgz", - "integrity": "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA==", - "license": "MIT" - }, - "node_modules/astro/node_modules/@astrojs/markdown-remark": { - "version": "6.3.10", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.10.tgz", - "integrity": "sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A==", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.7.5", - "@astrojs/prism": "3.3.0", - "github-slugger": "^2.0.0", - "hast-util-from-html": "^2.0.3", - "hast-util-to-text": "^4.0.2", - "import-meta-resolve": "^4.2.0", - "js-yaml": "^4.1.1", - "mdast-util-definitions": "^6.0.0", - "rehype-raw": "^7.0.0", - "rehype-stringify": "^10.0.1", - "remark-gfm": "^4.0.1", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.1.2", - "remark-smartypants": "^3.0.2", - "shiki": "^3.19.0", - "smol-toml": "^1.5.2", - "unified": "^11.0.5", - "unist-util-remove-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "unist-util-visit-parents": "^6.0.2", - "vfile": "^6.0.3" - } - }, "node_modules/astro/node_modules/@img/sharp-darwin-arm64": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", diff --git a/docs/package.json b/docs/package.json index 702cd13b2..0e8ab1f8e 100644 --- a/docs/package.json +++ b/docs/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@astrojs/starlight": "^0.34.0", - "astro": "5.17.3", + "astro": "^5.18.1", "sharp": "^0.33.0" }, "overrides": { diff --git a/docs/src/content/docs/chat-sessions.md b/docs/src/content/docs/chat-sessions.md index d89c14001..c4942e614 100644 --- a/docs/src/content/docs/chat-sessions.md +++ b/docs/src/content/docs/chat-sessions.md @@ -241,4 +241,4 @@ Map> allThreads = data.getThreads(); ## Sample -See the [chat-session sample](https://github.com/xavidop/genkit-java/tree/main/samples/chat-session) for a complete multi-turn chat implementation with state, tools, and session persistence. +See the [chat-session sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/chat-session) for a complete multi-turn chat implementation with state, tools, and session persistence. diff --git a/docs/src/content/docs/evaluations.md b/docs/src/content/docs/evaluations.md index ce19a171b..8ea5d6f10 100644 --- a/docs/src/content/docs/evaluations.md +++ b/docs/src/content/docs/evaluations.md @@ -255,5 +255,5 @@ See the [evaluators plugin docs](/genkit-java/plugins/evaluators/) for full deta ## Samples -- [evaluations sample](https://github.com/xavidop/genkit-java/tree/main/samples/evaluations) — Custom evaluators, dataset management, and running evaluations -- [evaluators-plugin sample](https://github.com/xavidop/genkit-java/tree/main/samples/evaluators-plugin) — Pre-built RAGAS metrics +- [evaluations sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/evaluations) — Custom evaluators, dataset management, and running evaluations +- [evaluators-plugin sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/evaluators-plugin) — Pre-built RAGAS metrics diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index 97656432f..f8ad63954 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -254,7 +254,7 @@ The fastest way to build AI features into your Java apps. - + diff --git a/docs/src/content/docs/interrupts.md b/docs/src/content/docs/interrupts.md index 0f02bd85c..747b424c1 100644 --- a/docs/src/content/docs/interrupts.md +++ b/docs/src/content/docs/interrupts.md @@ -189,4 +189,4 @@ if (chat.hasPendingInterrupts()) { ## Sample -See the [interrupts sample](https://github.com/xavidop/genkit-java/tree/main/samples/interrupts) for a complete banking transfer confirmation example. +See the [interrupts sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/interrupts) for a complete banking transfer confirmation example. diff --git a/docs/src/content/docs/middleware.md b/docs/src/content/docs/middleware.md index a125e43e4..216848067 100644 --- a/docs/src/content/docs/middleware.md +++ b/docs/src/content/docs/middleware.md @@ -226,5 +226,5 @@ Middleware metricsMiddleware = (request, context, next) -> { ## Samples -- [middleware sample](https://github.com/xavidop/genkit-java/tree/main/samples/middleware) — Custom and built-in middleware patterns -- [middleware-v2 sample](https://github.com/xavidop/genkit-java/tree/main/samples/middleware-v2) — Updated middleware approaches +- [middleware sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/middleware) — Custom and built-in middleware patterns +- [middleware-v2 sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/middleware-v2) — Updated middleware approaches diff --git a/docs/src/content/docs/multi-agent.md b/docs/src/content/docs/multi-agent.md index 7b0228a34..e137d6dc7 100644 --- a/docs/src/content/docs/multi-agent.md +++ b/docs/src/content/docs/multi-agent.md @@ -152,4 +152,4 @@ Multiple agents propose different solutions, then a judge agent selects the best ## Sample -See the [multi-agent sample](https://github.com/xavidop/genkit-java/tree/main/samples/multi-agent) for a complete restaurant assistant with triage, reservation, and menu agents. +See the [multi-agent sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/multi-agent) for a complete restaurant assistant with triage, reservation, and menu agents. diff --git a/docs/src/content/docs/plugins/firebase-functions.md b/docs/src/content/docs/plugins/firebase-functions.md index 57f1505ee..3057ec16d 100644 --- a/docs/src/content/docs/plugins/firebase-functions.md +++ b/docs/src/content/docs/plugins/firebase-functions.md @@ -120,4 +120,4 @@ firebase deploy --only functions ## Sample -See the [firebase sample](https://github.com/xavidop/genkit-java/tree/main/samples/firebase) for a Cloud Functions deployment example. +See the [firebase sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/firebase) for a Cloud Functions deployment example. diff --git a/docs/src/content/docs/plugins/firebase-vector-store.md b/docs/src/content/docs/plugins/firebase-vector-store.md index c9bc5031c..9d15a1548 100644 --- a/docs/src/content/docs/plugins/firebase-vector-store.md +++ b/docs/src/content/docs/plugins/firebase-vector-store.md @@ -120,4 +120,4 @@ gcloud firestore indexes composite create \ ## Sample -See the [firebase sample](https://github.com/xavidop/genkit-java/tree/main/samples/firebase) for a complete RAG pipeline with Firestore vector search. +See the [firebase sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/firebase) for a complete RAG pipeline with Firestore vector search. diff --git a/docs/src/content/docs/plugins/firebase.md b/docs/src/content/docs/plugins/firebase.md index a56baa068..55c8df8a3 100644 --- a/docs/src/content/docs/plugins/firebase.md +++ b/docs/src/content/docs/plugins/firebase.md @@ -46,4 +46,4 @@ Enable these APIs in your Google Cloud project: ## Sample -See the [firebase sample](https://github.com/xavidop/genkit-java/tree/main/samples/firebase) for a complete RAG pipeline with Firestore vector search and a Cloud Functions deployment example. +See the [firebase sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/firebase) for a complete RAG pipeline with Firestore vector search and a Cloud Functions deployment example. From f5baf8410e066427021f8cd606efc0a4d8199ebf Mon Sep 17 00:00:00 2001 From: xavidop Date: Tue, 31 Mar 2026 10:07:10 +0200 Subject: [PATCH 20/50] feat: add configuration for maven --- docs/src/content/docs/getting-started.mdx | 50 +++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/docs/src/content/docs/getting-started.mdx b/docs/src/content/docs/getting-started.mdx index cb1a87213..77f91b992 100644 --- a/docs/src/content/docs/getting-started.mdx +++ b/docs/src/content/docs/getting-started.mdx @@ -11,6 +11,56 @@ This guide shows you how to set up a Genkit Java project and make your first AI - **Maven 3.6+** - An API key from your preferred model provider (e.g., OpenAI, Google AI) +## Configure the Maven Repository + +Genkit Java artifacts are published to **GitHub Packages**. Even though the packages are public, you need to configure the repository in your project and provide authentication. + +### 1. Add the GitHub Packages repository + +Add the following repository to your `pom.xml`: + +```xml + + + github + https://maven.pkg.github.com/genkit-ai/genkit-java + + true + + + +``` + +### 2. Configure authentication + +GitHub Packages requires authentication even for public packages. Create or edit your `~/.m2/settings.xml` file: + +```xml + + + + github + YOUR_GITHUB_USERNAME + YOUR_GITHUB_TOKEN + + + +``` + +Replace `YOUR_GITHUB_USERNAME` with your GitHub username and `YOUR_GITHUB_TOKEN` with a [personal access token (classic)](https://github.com/settings/tokens) that has at least the **`read:packages`** scope. + +:::tip +You can also use environment variables to avoid hardcoding credentials: + +```xml + + github + ${env.GITHUB_ACTOR} + ${env.GITHUB_TOKEN} + +``` +::: + ## Installation Add the following dependencies to your Maven `pom.xml`: From 9d55f0c7029860b1dbb43e1f13ef7dfa1bd01fa3 Mon Sep 17 00:00:00 2001 From: xavidop Date: Thu, 2 Apr 2026 22:02:12 +0200 Subject: [PATCH 21/50] feat: agent skills --- docs/astro.config.mjs | 1 + docs/src/content/docs/claude-code-skills.md | 88 ++ .../SKILL.md | 1037 +++++++++++++++++ skills/developing-genkit-java/SKILL.md | 760 ++++++++++++ 4 files changed, 1886 insertions(+) create mode 100644 docs/src/content/docs/claude-code-skills.md create mode 100644 skills/building-ai-apps-with-genkit-java/SKILL.md create mode 100644 skills/developing-genkit-java/SKILL.md diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 68be997e2..725f1830b 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -124,6 +124,7 @@ export default defineConfig({ { label: "Dev UI & CLI", slug: "dev-ui" }, { label: "Architecture", slug: "architecture" }, { label: "Samples", slug: "samples" }, + { label: "Claude Code Skills", slug: "claude-code-skills" }, ], }, { diff --git a/docs/src/content/docs/claude-code-skills.md b/docs/src/content/docs/claude-code-skills.md new file mode 100644 index 000000000..e0c861f7c --- /dev/null +++ b/docs/src/content/docs/claude-code-skills.md @@ -0,0 +1,88 @@ +--- +title: Claude Code Skills +description: Use Genkit Java skills with Claude Code for AI-assisted development. +--- + +Genkit Java ships with [Claude Code skills](https://docs.anthropic.com/en/docs/claude-code/skills) — structured knowledge files that teach Claude Code how to work effectively with the framework. + +## Available skills + +| Skill | Description | Audience | +|-------|-------------|----------| +| **building-ai-apps-with-genkit-java** | Build AI-powered apps with Genkit Java: setup, flows, generation, tools, RAG, prompts, agents, deployment | App developers | +| **developing-genkit-java** | Contribute to the framework: architecture, plugin development, core abstractions, conventions | Framework contributors | + +## Installation + +### Option 1: Install from GitHub + +Install a specific skill: + +```bash +# For app developers +npx skills add https://github.com/genkit-ai/genkit-java --skill building-ai-apps-with-genkit-java + +# For framework contributors +npx skills add https://github.com/genkit-ai/genkit-java --skill developing-genkit-java +``` + +Skills are sourced from the [`skills/`](https://github.com/genkit-ai/genkit-java/tree/main/skills) directory and installed into your local Claude Code configuration. + +### Option 2: Manual setup + +Copy the skill folders into your Claude Code skills directory: + +```bash +# Clone the repo (if not already) +git clone https://github.com/genkit-ai/genkit-java.git + +# Copy skills to your Claude Code config +cp -r genkit-java/skills/* ~/.claude/skills/ +``` + +## Usage + +Once installed, Claude Code automatically activates the relevant skill based on your prompts. For example: + +- *"Create a new Genkit Java app with OpenAI that summarizes text"* → triggers the **building-ai-apps-with-genkit-java** skill +- *"Add a new Anthropic plugin to the Genkit Java framework"* → triggers the **developing-genkit-java** skill + +You can also invoke a skill explicitly in Claude Code: + +``` +/skill building-ai-apps-with-genkit-java "Build a RAG pipeline with Pinecone and Gemini" +``` + +## What's inside + +### building-ai-apps-with-genkit-java + +Covers everything an app developer needs: + +- **Quick start** — minimal `pom.xml`, application code, run commands +- **Provider setup** — all 12+ providers with Maven coordinates, env vars, model names +- **Generation API** — text, streaming, structured output, multi-turn +- **Flows** — defining, exposing as HTTP endpoints +- **Tools** — function calling with typed I/O +- **DotPrompt** — `.prompt` files with Handlebars templates and variants +- **RAG** — embeddings, indexing, retrieval with local and production vector stores +- **Sessions & Chat** — multi-turn conversations with state +- **Agents** — multi-agent orchestration +- **Interrupts** — human-in-the-loop patterns +- **Evaluations** — built-in RAGAS metrics and custom evaluators +- **Deployment** — Jetty, Spring Boot, Firebase Cloud Functions +- **Observability** — tracing, metrics, Firebase telemetry + +### developing-genkit-java + +Covers framework internals and contribution guidelines: + +- **Module architecture** — core, ai, genkit, plugins dependency flow +- **Core abstractions** — `Action`, `ActionContext`, `Registry`, `ActionType` +- **Plugin system** — `Plugin` interface, registration, `compat-oai` base +- **Model implementation** — `Model` interface, streaming, SSE +- **Naming conventions** — packages, classes, methods, action keys +- **Testing patterns** — JUnit 5, Mockito, test structure +- **Code quality** — Google Java Format, Checkstyle +- **Conventional commits** — types, scopes, versioning +- **New module checklist** — step-by-step guide for adding plugins diff --git a/skills/building-ai-apps-with-genkit-java/SKILL.md b/skills/building-ai-apps-with-genkit-java/SKILL.md new file mode 100644 index 000000000..dd1259746 --- /dev/null +++ b/skills/building-ai-apps-with-genkit-java/SKILL.md @@ -0,0 +1,1037 @@ +--- +name: building-ai-apps-with-genkit-java +description: Guide for building AI-powered Java applications using the Genkit Java framework. Use this skill when the user wants to create a new AI app, add AI features to an existing Java project, define flows, call models, use tools, build RAG pipelines, manage prompts, handle structured output, set up multi-turn chat, create agents, run evaluations, or deploy with Genkit Java. Covers all supported providers (OpenAI, Google Gemini, Anthropic, Ollama, AWS Bedrock, Azure, and more). +argument-hint: Describe what you want to build (e.g., "a chatbot with RAG", "a Spring Boot app with Gemini", "structured output with OpenAI") +--- + +# Building AI Applications with Genkit Java + +You are helping a developer build AI-powered applications using **Genkit Java**, the open-source Java AI framework by Google. This skill covers everything an end user needs: setup, configuration, all APIs, providers, patterns, and deployment. + +--- + +## Quick Start + +### Prerequisites + +- **Java 21+** +- **Maven** +- **API key** for your chosen provider (OpenAI, Google, Anthropic, etc.) +- **Genkit CLI** (optional, for Dev UI): `npm install -g genkit` + +### Minimal pom.xml + +```xml + + 4.0.0 + com.example + my-ai-app + 1.0-SNAPSHOT + + + 21 + 21 + 1.0.0-SNAPSHOT + + + + + + com.google.genkit + genkit + ${genkit.version} + + + + + com.google.genkit + genkit-plugin-openai + ${genkit.version} + + + + + com.google.genkit + genkit-plugin-jetty + ${genkit.version} + + + + + ch.qos.logback + logback-classic + 1.5.32 + + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.5.0 + + com.example.MyApp + + + + + +``` + +### Minimal Application + +```java +package com.example; + +import com.google.genkit.Genkit; +import com.google.genkit.core.GenkitOptions; +import com.google.genkit.ai.GenerateOptions; +import com.google.genkit.ai.GenerationConfig; +import com.google.genkit.ai.model.ModelResponse; +import com.google.genkit.core.flow.Flow; +import com.google.genkit.plugins.openai.OpenAIPlugin; +import com.google.genkit.plugins.jetty.JettyPlugin; +import com.google.genkit.plugins.jetty.JettyPluginOptions; + +public class MyApp { + public static void main(String[] args) throws Exception { + JettyPlugin jetty = new JettyPlugin( + JettyPluginOptions.builder().port(8080).build()); + + Genkit genkit = Genkit.builder() + .options(GenkitOptions.builder() + .devMode(true) + .reflectionPort(3100) + .build()) + .plugin(OpenAIPlugin.create()) + .plugin(jetty) + .build(); + + genkit.defineFlow("ask", String.class, String.class, + (ctx, question) -> genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt(question) + .build()).getText()); + + genkit.init(); + } +} +``` + +### Run It + +```bash +export OPENAI_API_KEY=sk-... +mvn compile exec:java + +# Or with Dev UI (recommended) +genkit start -- mvn compile exec:java +``` + +### Test It + +```bash +curl -X POST http://localhost:8080/ask \ + -H 'Content-Type: application/json' \ + -d '"What is the capital of France?"' +``` + +--- + +## Provider Setup + +### Maven Artifacts (all `com.google.genkit`, version `1.0.0-SNAPSHOT`) + +| Provider | Artifact | Env Var | Plugin Init | +|----------|----------|---------|-------------| +| OpenAI | `genkit-plugin-openai` | `OPENAI_API_KEY` | `OpenAIPlugin.create()` | +| Google Gemini | `genkit-plugin-google-genai` | `GOOGLE_GENAI_API_KEY` | `GoogleGenAIPlugin.create()` | +| Anthropic | `genkit-plugin-anthropic` | `ANTHROPIC_API_KEY` | `AnthropicPlugin.create()` | +| Ollama | `genkit-plugin-ollama` | none (local) | `OllamaPlugin.create("gemma3n:e4b")` | +| AWS Bedrock | `genkit-plugin-aws-bedrock` | AWS credentials | `AwsBedrockPlugin.create("us-east-1")` | +| Azure Foundry | `genkit-plugin-azure-foundry` | Azure credentials | `AzureFoundryPlugin.create()` | +| DeepSeek | `genkit-plugin-deepseek` | `DEEPSEEK_API_KEY` | `DeepSeekPlugin.create()` | +| Mistral | `genkit-plugin-mistral` | `MISTRAL_API_KEY` | `MistralPlugin.create()` | +| Groq | `genkit-plugin-groq` | `GROQ_API_KEY` | `GroqPlugin.create()` | +| Cohere | `genkit-plugin-cohere` | `COHERE_API_KEY` | `CoherePlugin.create()` | +| xAI | `genkit-plugin-xai` | `XAI_API_KEY` | `XAIPlugin.create()` | +| Any OpenAI-compatible | `genkit-plugin-compat-oai` | varies | `CompatOAIPlugin.create(options)` | + +### Model Names by Provider + +``` +# OpenAI +openai/gpt-4o, openai/gpt-4o-mini, openai/gpt-4-turbo, openai/gpt-3.5-turbo +openai/o1-preview, openai/o1-mini +openai/text-embedding-3-small, openai/text-embedding-3-large +openai/dall-e-3, openai/dall-e-2, openai/gpt-image-1 + +# Google Gemini +googleai/gemini-2.0-flash, googleai/gemini-1.5-pro, googleai/gemini-1.5-flash +googleai/text-embedding-004, googleai/imagen-3.0-generate-002 + +# Anthropic +anthropic/claude-sonnet-4-5-20250929, anthropic/claude-opus-4-5-20251101 +anthropic/claude-haiku-4-5-20251001 +anthropic/claude-opus-4-1, anthropic/claude-sonnet-4 + +# Ollama (any model you have pulled) +ollama/gemma3n:e4b, ollama/llama3, ollama/mistral + +# AWS Bedrock +aws-bedrock/amazon.nova-lite-v1:0, aws-bedrock/amazon.nova-pro-v1:0 +aws-bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0 +aws-bedrock/meta.llama3-2-90b-instruct-v1:0 +``` + +### Passing API Keys Programmatically + +```java +// Instead of environment variables +OpenAIPlugin.create("sk-your-key-here") +AnthropicPlugin.create("sk-ant-your-key-here") +GoogleGenAIPlugin.create("AIza...") +``` + +--- + +## Server Options + +### Jetty (Lightweight) + +```xml + + com.google.genkit + genkit-plugin-jetty + ${genkit.version} + +``` + +```java +JettyPlugin jetty = new JettyPlugin( + JettyPluginOptions.builder().port(8080).build()); + +// Flows are exposed at: POST http://localhost:8080/{flowName} +``` + +### Spring Boot + +```xml + + com.google.genkit + genkit-plugin-spring + ${genkit.version} + +``` + +```java +// Flows exposed at: POST http://localhost:8080/api/flows/{flowName} +// Health check: GET http://localhost:8080/health +// List flows: GET http://localhost:8080/api/flows +``` + +--- + +## Defining Flows + +Flows are observable, HTTP-callable functions that form the backbone of your app. + +### Simple Flow (no AI context needed) + +```java +genkit.defineFlow("greet", String.class, String.class, + (name) -> "Hello, " + name + "!"); +``` + +### Flow with AI Generation + +```java +genkit.defineFlow("summarize", String.class, String.class, + (ctx, text) -> genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("Summarize this: " + text) + .build()).getText()); +``` + +### Flow with Custom Input/Output Types + +```java +public record TranslateInput(String text, String targetLanguage) {} +public record TranslateOutput(String translation, String detectedLanguage) {} + +genkit.defineFlow("translate", TranslateInput.class, TranslateOutput.class, + (ctx, input) -> { + ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("Translate to " + input.targetLanguage() + ": " + input.text()) + .outputClass(TranslateOutput.class) + .build()); + return response.getOutput(); + }); +``` + +### Flow with Middleware + +```java +genkit.defineFlow("secured", String.class, String.class, + (ctx, input) -> processInput(input), + List.of(loggingMiddleware, authMiddleware)); +``` + +--- + +## Generation API + +### Basic Text Generation + +```java +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("Explain quantum computing in simple terms") + .build()); + +String text = response.getText(); +``` + +### With Configuration + +```java +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("Write a creative poem") + .config(GenerationConfig.builder() + .temperature(0.9) + .maxOutputTokens(2048) + .topP(0.95) + .topK(40) + .stopSequences(List.of("\n\n\n")) + .build()) + .build()); +``` + +### System Prompt + +```java +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .system("You are a pirate. Respond in pirate speak.") + .prompt("How do I cook pasta?") + .build()); +``` + +### Multi-Turn Conversation + +```java +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .messages(List.of( + Message.system("You are a helpful math tutor."), + Message.user("What is 2+2?"), + Message.model("2+2 equals 4."), + Message.user("What about 2+2+2?"))) + .build()); +``` + +### Streaming + +```java +ModelResponse response = genkit.generateStream( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("Write a long story about a dragon") + .build(), + chunk -> System.out.print(chunk.getText())); // Print as it arrives +``` + +### Structured Output (JSON) + +Use `@JsonProperty` and `@JsonPropertyDescription` for schema generation: + +```java +public class Recipe { + @JsonProperty(required = true) + @JsonPropertyDescription("Name of the recipe") + private String title; + + @JsonProperty(required = true) + @JsonPropertyDescription("List of ingredients with quantities") + private List ingredients; + + @JsonProperty(required = true) + @JsonPropertyDescription("Step-by-step instructions") + private List steps; + + @JsonPropertyDescription("Preparation time in minutes") + private int prepTime; + + // getters and setters +} + +Recipe recipe = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("Create a pasta carbonara recipe") + .outputClass(Recipe.class) + .build()); +``` + +### Token Usage + +```java +ModelResponse response = genkit.generate(...); +Usage usage = response.getUsage(); +System.out.println("Input tokens: " + usage.getInputTokens()); +System.out.println("Output tokens: " + usage.getOutputTokens()); +``` + +--- + +## Tools (Function Calling) + +Let models call your Java functions: + +```java +public record WeatherInput(String location) {} +public record WeatherOutput(String temperature, String conditions) {} + +Tool weatherTool = genkit.defineTool( + "getWeather", + "Get current weather for a location", + (ctx, input) -> new WeatherOutput("72°F", "Sunny in " + input.location()), + WeatherInput.class, WeatherOutput.class); + +// The model will call the tool automatically when needed +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("What's the weather in London and Tokyo?") + .tools(List.of(weatherTool)) + .build()); +``` + +### Multiple Tools + +```java +Tool searchTool = genkit.defineTool( + "search", "Search the web", searchHandler, SearchInput.class, SearchOutput.class); + +Tool calcTool = genkit.defineTool( + "calculate", "Perform math", calcHandler, CalcInput.class, CalcOutput.class); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("Search for the population of France and calculate its density") + .tools(List.of(searchTool, calcTool)) + .build()); +``` + +--- + +## DotPrompt — Prompt Files + +Manage prompts as files with YAML frontmatter + Handlebars templates. + +### Create a Prompt File + +Place in `src/main/resources/prompts/recipe.prompt`: + +``` +--- +model: openai/gpt-4o-mini +config: + temperature: 0.9 + maxOutputTokens: 500 +input: + schema: + food: string + ingredients?(array): string +output: + format: json + schema: + title: string, recipe title + ingredients(array): + name: string + quantity: string + steps(array): string + prepTime: string + cookTime: string + servings: integer +--- + +You are a chef famous for creative recipes prepared in 45 minutes or less. + +Generate a detailed recipe for {{food}}. + +{{#if ingredients}} +Make sure to include: +{{#each ingredients}} +- {{this}} +{{/each}} +{{/if}} + +Provide the recipe in the exact JSON format specified in the output schema. +``` + +### Use It in Code + +```java +public record RecipeInput(String food, List ingredients) {} + +ExecutablePrompt recipePrompt = genkit.prompt("recipe", RecipeInput.class); +ModelResponse response = recipePrompt.generate(new RecipeInput("pasta", List.of("tomatoes", "basil"))); +``` + +### Prompt Variants + +Create alternate versions of the same prompt: + +- `recipe.prompt` — default +- `recipe.robot.prompt` — robot variant + +```java +ExecutablePrompt robotRecipe = genkit.prompt("recipe", RecipeInput.class, "robot"); +``` + +### Partials (Reusable Fragments) + +Create `src/main/resources/prompts/_style.prompt`: + +``` +You always respond with enthusiasm and use exclamation marks! +``` + +Reference in other prompts: + +``` +{{> _style}} +Now generate a recipe for {{food}}. +``` + +--- + +## RAG (Retrieval-Augmented Generation) + +### Local Development with LocalVec + +```xml + + com.google.genkit + genkit-plugin-localvec + ${genkit.version} + +``` + +```java +Genkit genkit = Genkit.builder() + .plugin(OpenAIPlugin.create()) + .plugin(LocalVecPlugin.builder() + .addStore(LocalVecConfig.builder() + .indexName("my-docs") + .embedderName("openai/text-embedding-3-small") + .directory("/tmp/genkit-vectors") + .build()) + .build()) + .plugin(jetty) + .build(); + +// Index documents +genkit.defineFlow("indexDocs", Void.class, String.class, + (ctx, input) -> { + List docs = List.of( + Document.fromText("Paris is the capital of France."), + Document.fromText("Berlin is the capital of Germany."), + Document.fromText("Tokyo is the capital of Japan.")); + genkit.index("devLocalVectorStore/my-docs", docs); + return "Indexed " + docs.size() + " documents"; + }); + +// RAG query +genkit.defineFlow("ragQuery", String.class, String.class, + (ctx, query) -> { + List context = genkit.retrieve("devLocalVectorStore/my-docs", query); + return genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt(query) + .docs(context) + .build()).getText(); + }); +``` + +### Production Vector Stores + +```xml + +genkit-plugin-firebase +genkit-plugin-pinecone +genkit-plugin-postgresql +genkit-plugin-weaviate +``` + +### Embeddings Directly + +```java +EmbedResponse embeddings = genkit.embed( + "openai/text-embedding-3-small", + List.of(Document.fromText("Hello world"))); +``` + +--- + +## Sessions & Multi-Turn Chat + +```java +Session session = genkit.createSession(); + +Chat chat = genkit.chat(ChatOptions.builder() + .model("openai/gpt-4o") + .session(session) + .system("You are a helpful assistant.") + .build()); + +// Each call maintains conversation history +ModelResponse r1 = chat.send("What is the capital of France?"); +ModelResponse r2 = chat.send("What about Germany?"); // Remembers context +``` + +--- + +## Agents + +Create specialized AI agents that can use tools and delegate to each other: + +```java +Tool reserveTool = genkit.defineTool( + "makeReservation", "Book a restaurant reservation", + reserveHandler, ReservationInput.class, ReservationOutput.class); + +Tool menuTool = genkit.defineTool( + "getMenu", "Get restaurant menu", + menuHandler, MenuInput.class, MenuOutput.class); + +Agent reservationAgent = genkit.defineAgent(AgentConfig.builder() + .name("reservationAgent") + .model("openai/gpt-4o") + .description("Handles restaurant reservations") + .tools(List.of(reserveTool)) + .build()); + +Agent menuAgent = genkit.defineAgent(AgentConfig.builder() + .name("menuAgent") + .model("openai/gpt-4o") + .description("Answers menu questions") + .tools(List.of(menuTool)) + .build()); + +// Triage agent routes to specialists +Agent triageAgent = genkit.defineAgent(AgentConfig.builder() + .name("triageAgent") + .model("openai/gpt-4o") + .description("Routes requests to the appropriate specialist") + .tools(List.of( + genkit.getAllToolsForAgent(reservationAgent), + genkit.getAllToolsForAgent(menuAgent))) + .build()); +``` + +--- + +## Interrupts (Human-in-the-Loop) + +Pause execution and wait for user confirmation: + +```java +Tool confirmTool = genkit.defineInterrupt( + InterruptConfig.builder() + .name("confirmTransfer") + .inputClass(ConfirmInput.class) + .outputClass(ConfirmOutput.class) + .build()); + +// In a flow, use ctx.interrupt() to pause +genkit.defineFlow("transfer", TransferRequest.class, String.class, + (ctx, input) -> { + if (input.amount() > 1000) { + ctx.interrupt(InterruptRequest.builder() + .type("CONFIRMATION") + .data(Map.of("action", "TRANSFER", "amount", input.amount())) + .build()); + } + return performTransfer(input); + }); + +// Resume with user response +genkit.generate(GenerateOptions.builder() + .model("openai/gpt-4o") + .resume(ResumeOptions.builder() + .sessionId(sessionId) + .response(userConfirmation) + .build()) + .build()); +``` + +--- + +## Middleware + +Add cross-cutting concerns to flows: + +```java +// Logging middleware +Middleware logger = (input, ctx, next) -> { + System.out.println("Input: " + input); + String result = next.handle(input, ctx); + System.out.println("Output: " + result); + return result; +}; + +// Apply to flow +genkit.defineFlow("myFlow", String.class, String.class, + handler, List.of(logger)); +``` + +--- + +## MCP (Model Context Protocol) + +Connect to MCP tool servers: + +```xml + + com.google.genkit + genkit-plugin-mcp + ${genkit.version} + +``` + +```java +MCPPlugin mcpPlugin = MCPPlugin.create(MCPPluginOptions.builder() + .addServer(MCPServerConfig.stdio( + "npx", "@modelcontextprotocol/server-filesystem", "/tmp")) + .build()); + +Genkit genkit = Genkit.builder() + .plugin(mcpPlugin) + .plugin(OpenAIPlugin.create()) + .build(); + +// MCP tools are auto-registered and available for generation +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("List files in /tmp") + .tools(mcpPlugin.getTools()) + .build()); + +// Access MCP resources +List resources = mcpPlugin.getResources(); +MCPResourceContent content = mcpPlugin.readResource("file:///tmp/data.txt"); +``` + +--- + +## Evaluations + +### Built-in RAGAS Evaluators + +```xml + + com.google.genkit + genkit-plugin-evaluators + ${genkit.version} + +``` + +```java +Genkit genkit = Genkit.builder() + .plugin(OpenAIPlugin.create()) + .plugin(EvaluatorsPlugin.create( + EvaluatorsPluginOptions.builder() + .judge("openai/gpt-4o-mini") + .metrics(List.of( + GenkitMetric.FAITHFULNESS, + GenkitMetric.ANSWER_RELEVANCY, + GenkitMetric.ANSWER_ACCURACY, + GenkitMetric.MALICIOUSNESS)) + .build())) + .build(); +``` + +Available metrics: `FAITHFULNESS`, `ANSWER_RELEVANCY`, `ANSWER_ACCURACY`, `MALICIOUSNESS`, `REGEX`, `DEEP_EQUAL`, `JSONATA` + +### Custom Evaluators + +```java +Evaluator lengthEval = genkit.defineEvaluator( + "custom/length", "Length Check", "Checks if output meets minimum length", + (dataPoint, options) -> { + int len = dataPoint.getOutput().length(); + boolean pass = len >= 100; + return EvalResponse.builder() + .score(pass ? 1.0 : 0.0) + .rationale(pass ? "Adequate length" : "Too short: " + len + " chars") + .build(); + }); +``` + +### Run Evaluations via Dev UI + +Start with `genkit start -- ./run.sh`, open http://localhost:4000, and use the Evaluations tab to create datasets and run evaluations interactively. + +--- + +## Firebase Deployment + +### Firestore Vector Search (RAG) + +```java +Genkit genkit = Genkit.builder() + .plugin(GoogleGenAIPlugin.create()) + .plugin(FirebasePlugin.builder() + .projectId("my-project") + .addRetriever(FirestoreRetrieverConfig.builder() + .name("my-docs") + .collection("documents") + .embedderName("googleai/text-embedding-004") + .vectorField("embedding") + .contentField("text") + .build()) + .enableTelemetry(true) // Cloud Trace + Monitoring + Logging + .build()) + .build(); + +List docs = genkit.retrieve("firebase/my-docs", "search query"); +``` + +### Cloud Functions + +```java +public class MyFunction implements HttpFunction { + private final OnCallGenkit genkitFunction; + + public MyFunction() { + Genkit genkit = Genkit.builder() + .plugin(GoogleGenAIPlugin.create(System.getenv("GEMINI_API_KEY"))) + .plugin(FirebasePlugin.builder().build()) + .build(); + + genkit.defineFlow("myFlow", String.class, String.class, + (ctx, input) -> genkit.generate( + GenerateOptions.builder() + .model("googleai/gemini-2.0-flash") + .prompt(input) + .build()).getText()); + + genkit.init(); + this.genkitFunction = OnCallGenkit.fromFlow(genkit, "myFlow"); + } + + @Override + public void service(HttpRequest request, HttpResponse response) throws IOException { + genkitFunction.service(request, response); + } +} +``` + +--- + +## Observability + +### Built-in Tracing (OpenTelemetry) + +Every flow and generation call is automatically traced. In dev mode, traces are visible in the Dev UI at http://localhost:4000. + +### Token Metrics + +Automatic metrics exported via OpenTelemetry: + +- `genkit/ai/generate/requests` — generation call count +- `genkit/ai/generate/latency` — generation latency +- `genkit/ai/generate/input/tokens` — input token count +- `genkit/ai/generate/output/tokens` — output token count +- `genkit/feature/requests` — flow call count +- `genkit/feature/latency` — flow latency + +### Production Telemetry (Firebase) + +```java +FirebasePlugin.builder() + .projectId("my-project") + .enableTelemetry(true) // Exports to Cloud Trace, Monitoring, Logging + .build() +``` + +--- + +## Dev Workflow + +### Development Mode + +```java +GenkitOptions.builder() + .devMode(true) // Enable reflection server + .reflectionPort(3100) // Default port for dev tools + .build() +``` + +### Dev UI + +```bash +# Install CLI +npm install -g genkit + +# Start app with Dev UI +genkit start -- mvn compile exec:java + +# Open http://localhost:4000 +``` + +The Dev UI lets you: +- List and test all flows, models, tools, prompts, retrievers, evaluators +- Send test inputs and see outputs +- View full traces with timing and token counts +- Create evaluation datasets and run evaluations +- Inspect registered actions and their schemas + +### HTTP Endpoints + +```bash +# Jetty +curl -X POST http://localhost:8080/{flowName} \ + -H 'Content-Type: application/json' \ + -d '"your input"' + +# With complex input +curl -X POST http://localhost:8080/translate \ + -H 'Content-Type: application/json' \ + -d '{"text": "Hello", "targetLanguage": "Spanish"}' + +# Spring Boot +curl http://localhost:8080/health +curl http://localhost:8080/api/flows +curl -X POST http://localhost:8080/api/flows/{flowName} \ + -H 'Content-Type: application/json' \ + -d '"input"' +``` + +--- + +## Common Patterns + +### Error Handling + +```java +import com.google.genkit.core.GenkitException; + +genkit.defineFlow("safe", String.class, String.class, + (ctx, input) -> { + try { + return genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt(input) + .build()).getText(); + } catch (GenkitException e) { + // e.getErrorCode(), e.getDetails(), e.getTraceId() + return "Error: " + e.getMessage(); + } + }); +``` + +### Switching Providers + +Genkit is provider-agnostic. Change the model string and plugin: + +```java +// Switch from OpenAI to Gemini — just change plugin + model name +.plugin(GoogleGenAIPlugin.create()) +// ... +.model("googleai/gemini-2.0-flash") + +// Switch to Anthropic +.plugin(AnthropicPlugin.create()) +// ... +.model("anthropic/claude-sonnet-4-5-20250929") + +// Switch to local Ollama +.plugin(OllamaPlugin.create("gemma3n:e4b")) +// ... +.model("ollama/gemma3n:e4b") +``` + +### Multiple Providers in One App + +```java +Genkit genkit = Genkit.builder() + .plugin(OpenAIPlugin.create()) + .plugin(GoogleGenAIPlugin.create()) + .plugin(AnthropicPlugin.create()) + .build(); + +// Use different models for different tasks +genkit.defineFlow("quickAnswer", String.class, String.class, + (ctx, q) -> genkit.generate(GenerateOptions.builder() + .model("openai/gpt-4o-mini").prompt(q).build()).getText()); + +genkit.defineFlow("deepAnalysis", String.class, String.class, + (ctx, q) -> genkit.generate(GenerateOptions.builder() + .model("anthropic/claude-sonnet-4-5-20250929").prompt(q).build()).getText()); + +genkit.defineFlow("creative", String.class, String.class, + (ctx, q) -> genkit.generate(GenerateOptions.builder() + .model("googleai/gemini-2.0-flash").prompt(q).build()).getText()); +``` + +### Custom OpenAI-Compatible Endpoint + +```java +.plugin(CompatOAIPlugin.create(CompatOAIPluginOptions.builder() + .baseUrl("https://my-custom-endpoint.com/v1") + .apiKey("my-key") + .models(List.of("my-model")) + .build())) +``` + +--- + +## Project Structure Recommendation + +``` +my-ai-app/ +├── pom.xml +├── run.sh +├── src/main/ +│ ├── java/com/example/ +│ │ ├── MyApp.java # Genkit init + flow definitions +│ │ ├── flows/ # Complex flows in separate classes +│ │ │ ├── RagFlow.java +│ │ │ └── ChatFlow.java +│ │ └── tools/ # Tool implementations +│ │ ├── SearchTool.java +│ │ └── CalcTool.java +│ └── resources/ +│ ├── prompts/ # .prompt files +│ │ ├── recipe.prompt +│ │ └── recipe.robot.prompt +│ ├── data/ # RAG source data +│ │ └── knowledge-base.txt +│ └── logback.xml # Logging config +└── src/test/java/com/example/ # Tests +``` + +### run.sh + +```bash +#!/bin/bash +cd "$(dirname "$0")" +mvn compile exec:java +``` diff --git a/skills/developing-genkit-java/SKILL.md b/skills/developing-genkit-java/SKILL.md new file mode 100644 index 000000000..4298ca861 --- /dev/null +++ b/skills/developing-genkit-java/SKILL.md @@ -0,0 +1,760 @@ +--- +name: developing-genkit-java +description: Best practices for developing with and contributing to Genkit Java — the open-source Java AI framework by Google. Covers project architecture, plugin development, flow definition, model integration, RAG pipelines, testing, naming conventions, and code quality guidelines. Use this skill when the user asks about building AI applications in Java with Genkit, creating custom plugins, defining flows, working with models, embedders, retrievers, tools, prompts, agents, or contributing to the Genkit Java codebase. +argument-hint: Describe the Genkit Java task (e.g., "create an Anthropic plugin", "add a RAG flow", "define a tool") +--- + +# Developing with Genkit Java + +You are an expert on **Genkit Java**, the open-source Java AI framework by Google. This skill covers the full framework: architecture, plugin system, AI abstractions, and contribution guidelines. + +## Project Architecture + +Genkit Java is a Maven multi-module project requiring **Java 21+**. + +### Module Hierarchy + +``` +genkit-java/ +├── pom.xml # Parent POM (dependency management, plugins) +├── core/ # Foundational abstractions (Action, Flow, Registry, Plugin, Middleware, Tracing) +│ └── com.google.genkit.core +├── ai/ # AI-specific abstractions (Model, Tool, Embedder, Retriever, Indexer, Message, Part) +│ └── com.google.genkit.ai +├── genkit/ # High-level user-facing API (Genkit class, Prompts, Sessions, Agents, Evaluators) +│ └── com.google.genkit +├── plugins/ # Provider integrations (21 plugins) +│ └── com.google.genkit.plugins.{name} +└── samples/ # Example applications (20+ samples) + └── com.google.genkit.samples +``` + +### Dependency Flow + +``` +core ← ai ← genkit ← plugins ← samples +``` + +- **core** has zero Genkit internal dependencies. It depends on Jackson, SLF4J, OpenTelemetry, victools JSON Schema. +- **ai** depends on core. +- **genkit** depends on core + ai + Handlebars (for .prompt files). +- **plugins** depend on genkit (or ai/core). +- **samples** depend on genkit + chosen plugins. + +### Key Dependencies (Managed in Parent POM) + +| Library | Version | Purpose | +|---------|---------|---------| +| Jackson | 2.21.2 | JSON serialization (databind, annotations, jsr310) | +| SLF4J | 2.0.17 | Logging facade | +| Logback | 1.5.32 | Logging implementation | +| OkHttp | 5.3.2 | HTTP client + SSE streaming | +| OpenTelemetry | 1.60.1 | Tracing and metrics | +| Handlebars | 4.5.0 | .prompt file templating | +| victools | 4.38.0 | JSON Schema generation from Java classes | +| JUnit | 6.0.3 | Testing framework | +| Mockito | 5.23.0 | Mocking framework | + +--- + +## Core Abstractions + +### Action — The Universal Unit + +Every capability in Genkit is an `Action`: + +- `I` = Input type +- `O` = Output type +- `S` = Streaming chunk type (`Void` for non-streaming) + +```java +public interface Action extends Registerable { + String getName(); + ActionType getType(); + O run(ActionContext ctx, I input); + O run(ActionContext ctx, I input, Consumer streamCallback); +} +``` + +All AI primitives (Model, Tool, Embedder, Retriever, Indexer, Flow) implement `Action`. Actions self-register with the `Registry` using keys in the format `{type}/{name}` (e.g., `model/openai/gpt-4o`, `flow/myFlow`, `tool/getWeather`). + +### ActionType Enum + +```java +RETRIEVER, INDEXER, EMBEDDER, EVALUATOR, FLOW, MODEL, BACKGROUND_MODEL, +EXECUTABLE_PROMPT, PROMPT, RESOURCE, TOOL, TOOL_V2, UTIL, CUSTOM, +CHECK_OPERATION, CANCEL_OPERATION +``` + +### ActionContext + +Passed to every action execution. Carries tracing info, registry access, session state: + +```java +public class ActionContext { + SpanContext spanContext; + String flowName; + Registry registry; + String sessionId; +} +``` + +### Registry + +Centralized action discovery and lookup: + +```java +registry.registerAction(key, action); +registry.lookupAction("model/openai/gpt-4o"); +registry.lookupAction(ActionType.FLOW, "myFlow"); +``` + +--- + +## The Genkit Class — Main Entry Point + +The `Genkit` class is the high-level API. Always use the builder pattern: + +```java +Genkit genkit = Genkit.builder() + .options(GenkitOptions.builder() + .devMode(true) + .reflectionPort(3100) + .build()) + .plugin(new OpenAIPlugin()) + .plugin(new JettyPlugin()) + .build(); + +genkit.init(); // Initialize plugins, start servers +``` + +### Lifecycle + +1. `Genkit.builder()...build()` — creates instance, does NOT initialize +2. `genkit.init()` — calls `plugin.init(registry)` for each plugin, starts dev server if applicable +3. `genkit.stop()` — cleanup resources + +--- + +## Defining Flows + +Flows are user-defined actions exposed as HTTP endpoints: + +```java +// Simple (no context needed) +Flow greetFlow = genkit.defineFlow( + "greet", String.class, String.class, + (name) -> "Hello, " + name + "!"); + +// With ActionContext (for nested AI calls) +Flow jokeFlow = genkit.defineFlow( + "tellJoke", String.class, String.class, + (ctx, topic) -> { + ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("Tell a joke about: " + topic) + .config(GenerationConfig.builder().temperature(0.9).build()) + .build()); + return response.getText(); + }); + +// With middleware +Flow securedFlow = genkit.defineFlow( + "secured", String.class, String.class, + (ctx, input) -> processInput(input), + List.of(authMiddleware, loggingMiddleware)); +``` + +--- + +## Generation API + +### Simple Generation + +```java +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("Explain quantum computing") + .build()); +String text = response.getText(); +``` + +### Streaming Generation + +```java +ModelResponse response = genkit.generateStream( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("Write a story") + .build(), + chunk -> System.out.print(chunk.getText())); +``` + +### Structured Output + +```java +MyPojo result = genkit.generateObject( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("Generate a recipe for pasta") + .outputClass(MyPojo.class) + .build()); +``` + +### Multi-turn Messages + +```java +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .messages(List.of( + Message.system("You are a helpful assistant."), + Message.user("What is the capital of France?"), + Message.model("Paris is the capital of France."), + Message.user("What about Germany?"))) + .build()); +``` + +### GenerationConfig + +```java +GenerationConfig.builder() + .temperature(0.9) + .maxOutputTokens(2048) + .topK(40) + .topP(0.95) + .stopSequences(List.of("\n\n")) + .build() +``` + +--- + +## Tools — AI-Callable Functions + +Define tools that models can invoke: + +```java +// With auto-generated JSON Schema from classes +Tool weatherTool = genkit.defineTool( + "getWeather", + "Get current weather for a location", + (ctx, input) -> fetchWeather(input.getLocation()), + WeatherInput.class, WeatherOutput.class); + +// Use tools in generation +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt("What's the weather in London?") + .tools(List.of(weatherTool)) + .build()); +``` + +--- + +## RAG (Retrieval-Augmented Generation) + +### Embedding + +```java +EmbedResponse embeddings = genkit.embed( + "openai/text-embedding-3-small", + List.of(Document.fromText("Hello world"))); +``` + +### Indexing + +```java +genkit.index("devLocalVectorStore/my-index", documents); +``` + +### Retrieval + Generation + +```java +List context = genkit.retrieve("devLocalVectorStore/my-index", query); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o") + .prompt(query) + .docs(context) // Inject retrieved documents + .build()); +``` + +--- + +## DotPrompt — .prompt Files + +Prompt files live in `resources/prompts/` with Handlebars templates and YAML frontmatter: + +``` +--- +model: openai/gpt-4o-mini +config: + temperature: 0.9 + maxOutputTokens: 500 +input: + schema: + ingredient: string + style?: string +--- +Create a recipe using {{ingredient}} in a {{style}} style. +``` + +### Loading Prompts + +```java +ExecutablePrompt prompt = genkit.prompt("recipe", RecipeInput.class); + +// With variant (recipe.robot.prompt) +ExecutablePrompt robotPrompt = genkit.prompt("recipe", RecipeInput.class, "robot"); +``` + +--- + +## Sessions & Chat + +```java +Session session = genkit.createSession(); +Chat chat = genkit.chat(ChatOptions.builder() + .model("openai/gpt-4o") + .session(session) + .build()); +``` + +--- + +## Agents + +Multi-agent systems with tool delegation: + +```java +Agent researchAgent = genkit.defineAgent(AgentConfig.builder() + .name("researcher") + .model("openai/gpt-4o") + .description("Research specialist") + .tools(List.of(searchTool, summarizeTool)) + .build()); +``` + +--- + +## Interrupts — Human-in-the-Loop + +```java +Tool confirmTool = genkit.defineInterrupt( + InterruptConfig.builder() + .name("confirmAction") + .inputClass(ConfirmInput.class) + .outputClass(ConfirmOutput.class) + .build()); +``` + +--- + +## Evaluators + +```java +Evaluator factualityEval = genkit.defineEvaluator( + "factuality", "Factuality Check", "Checks factual accuracy", + (datapoint) -> { + // Return EvalResponse with score, rationale, detail + }); + +EvalRunKey result = genkit.evaluate(RunEvaluationRequest.builder() + .evaluators(List.of("factuality")) + .dataset(dataset) + .build()); +``` + +--- + +## Plugin Development + +### The Plugin Interface + +```java +public interface Plugin { + String getName(); + List> init(); + default List> init(Registry registry) { return init(); } +} +``` + +### Creating a New Plugin + +1. **Create module** under `plugins/{name}/` with its own `pom.xml`. +2. **Package**: `com.google.genkit.plugins.{name}` +3. **Implement** `Plugin` interface. +4. **Return actions** from `init()` — Models, Embedders, Tools, Retrievers, etc. + +### Standard Plugin Structure + +``` +plugins/my-provider/ +├── pom.xml +├── README.md +└── src/main/java/com/google/genkit/plugins/my_provider/ + ├── MyProviderPlugin.java # Plugin entry point + ├── MyProviderPluginOptions.java # Configuration POJO (builder pattern) + ├── MyProviderModel.java # Model implementation + ├── MyProviderEmbedder.java # Embedder (if applicable) + └── ... +``` + +### Plugin Implementation Pattern + +```java +public class MyProviderPlugin implements Plugin { + public static final List SUPPORTED_MODELS = List.of("model-a", "model-b"); + + private final MyProviderPluginOptions options; + + public MyProviderPlugin(MyProviderPluginOptions options) { + this.options = options; + } + + public static MyProviderPlugin create() { + return new MyProviderPlugin(MyProviderPluginOptions.builder().build()); + } + + @Override + public String getName() { + return "my-provider"; + } + + @Override + public List> init() { + List> actions = new ArrayList<>(); + for (String model : SUPPORTED_MODELS) { + actions.add(new MyProviderModel( + getName() + "/" + model, model, options)); + } + return actions; + } +} +``` + +### compat-oai — Shared OpenAI-Compatible Base + +Many plugins (OpenAI, Anthropic, XAI, DeepSeek, Mistral, Cohere, Groq) extend the `compat-oai` plugin which provides a shared base for OpenAI-compatible APIs. When building a plugin for an OpenAI-compatible provider, extend `CompatOAIPlugin` / `CompatOAIModel` instead of implementing from scratch. + +### Server Plugins + +- **JettyPlugin** — Lightweight HTTP server, exposes flows as endpoints +- **SpringPlugin** — Spring Boot integration with auto-generated REST endpoints (`GenkitFlowController`) + +--- + +## Model Implementation + +Models implement `Action`: + +```java +public class MyModel implements Model { + @Override + public ModelInfo getInfo() { return modelInfo; } + + @Override + public boolean supportsStreaming() { return true; } + + @Override + public ModelResponse run(ActionContext ctx, ModelRequest request) { + // Make HTTP call to provider API + // Map response to ModelResponse + } + + @Override + public ModelResponse run(ActionContext ctx, ModelRequest request, + Consumer streamCallback) { + // SSE streaming via OkHttp + } +} +``` + +### Message & Part Model + +```java +Message.user("Hello") // User message +Message.system("You are a helper") // System message +Message.model("Response text") // Model response +Message.tool(List.of(Part.toolResponse(...))) // Tool result + +Part.text("Hello") // Text content +Part.media("image/png", dataUrl) // Media content +Part.toolRequest(name, ref, input) // Tool call +Part.toolResponse(name, ref, output) // Tool result +``` + +--- + +## Middleware + +Cross-cutting concerns applied to flows: + +```java +@FunctionalInterface +public interface Middleware { + O handle(I request, ActionContext context, MiddlewareNext next) + throws GenkitException; +} + +// Example: logging middleware +Middleware logger = (input, ctx, next) -> { + log.info("Input: {}", input); + String result = next.handle(input, ctx); + log.info("Output: {}", result); + return result; +}; +``` + +--- + +## Tracing & Observability + +Genkit uses OpenTelemetry for tracing: + +```java +SpanMetadata metadata = SpanMetadata.builder() + .name("myOperation") + .type("model") + .build(); + +Tracer.runInNewSpan(ctx, metadata, input, (spanCtx, req) -> { + return doWork(req); +}); +``` + +--- + +## Naming Conventions + +### Packages + +``` +com.google.genkit # Main API +com.google.genkit.core # Core abstractions +com.google.genkit.ai # AI abstractions +com.google.genkit.prompt # Prompt system +com.google.genkit.plugins.{name} # Plugins (use underscores for multi-word: google_genai) +com.google.genkit.samples # Sample apps +``` + +### Classes + +| Category | Convention | Examples | +|----------|-----------|----------| +| Interfaces | Noun | `Action`, `Model`, `Plugin`, `Registry` | +| Implementations | Prefixed noun | `ActionDef`, `DefaultRegistry`, `OpenAIModel` | +| Data classes | NounNoun | `ModelRequest`, `ModelResponse`, `GenerateOptions` | +| Builders | Inner static class | `MyClass.Builder` with `MyClass.builder()` | +| Exceptions | `GenkitException` | Custom with errorCode, details, traceId | +| Plugins | `{Provider}Plugin` | `OpenAIPlugin`, `AnthropicPlugin` | +| Options | `{Provider}PluginOptions` | `OpenAIPluginOptions` | + +### Methods + +| Category | Convention | Examples | +|----------|-----------|----------| +| Getters | `get{Property}()` | `getName()`, `getType()` | +| Factories | `static create()`, `static builder()`, `static define()` | | +| Execution | `run()` | `action.run(ctx, input)` | +| Registration | `register{Thing}()` | `registerAction()`, `registerPlugin()` | +| Lookup | `lookup{Thing}()` | `lookupAction()`, `lookupPlugin()` | +| Definition | `define{Thing}()` | `defineFlow()`, `defineTool()`, `defineAgent()` | + +### Action Keys + +Format: `{type}/{name}` + +``` +flow/myFlow +model/openai/gpt-4o +tool/getWeather +embedder/openai/text-embedding-3-small +retriever/myStore/docs +executable-prompt/myPrompt +``` + +--- + +## Testing Patterns + +### Framework + +JUnit 5 (Jupiter) + Mockito. Tests mirror source structure in `src/test/java/`. + +### Typical Test + +```java +@Test +void testFlowExecution() { + Registry registry = new DefaultRegistry(); + + Flow flow = Flow.define( + registry, "testFlow", String.class, String.class, + (ctx, input) -> input.toUpperCase()); + + ActionContext ctx = new ActionContext(registry); + String result = flow.run(ctx, "hello"); + + assertEquals("HELLO", result); +} +``` + +### Test Conventions + +- One test class per production class +- Test class name: `{ClassName}Test.java` +- Method names: `test{Behavior}` or descriptive camelCase +- Use `@Test`, `@BeforeEach`, `@AfterEach` annotations +- Mock external dependencies with Mockito + +--- + +## Sample Application Structure + +``` +samples/{provider}/ +├── pom.xml # Dependencies: genkit, plugin, jetty/spring +├── README.md # Setup instructions +├── run.sh # Execution script +└── src/main/java/com/google/genkit/samples/ + └── {Provider}Sample.java +``` + +### Canonical Sample Pattern + +```java +public class MySample { + public static void main(String[] args) throws Exception { + JettyPlugin jetty = new JettyPlugin( + JettyPluginOptions.builder().port(8080).build()); + + Genkit genkit = Genkit.builder() + .options(GenkitOptions.builder().devMode(true).reflectionPort(3100).build()) + .plugin(MyProviderPlugin.create()) + .plugin(jetty) + .build(); + + // Define flows + genkit.defineFlow("myFlow", String.class, String.class, + (ctx, input) -> { + return genkit.generate(GenerateOptions.builder() + .model("provider/model-name") + .prompt(input) + .build()).getText(); + }); + + genkit.init(); + } +} +``` + +--- + +## Code Quality & Build + +### Commands + +```bash +# Full build +mvn clean install + +# Format code (Google Java Format) +mvn fmt:format + +# Check formatting +mvn fmt:check + +# Run tests +mvn test + +# Build specific module +mvn -pl core clean install + +# Build with dependencies +mvn -pl plugins/openai -am clean install +``` + +### Code Style + +- **Formatter**: Google Java Format 1.35.0 +- **Linter**: Checkstyle 13.4.0 (google_checks.xml) +- **Serialization**: Jackson `@JsonInclude(Include.NON_NULL)` — always omit null fields +- **Immutability**: Use builder pattern for configuration objects +- **Generics**: Preserve type safety with `Action` pattern throughout + +--- + +## Conventional Commits + +All commits follow the format: `(): ` + +### Types + +| Type | Purpose | Version Impact | +|------|---------|----------------| +| `feat` | New feature | Minor bump | +| `fix` | Bug fix | Patch bump | +| `docs` | Documentation only | None | +| `style` | Formatting, no code change | None | +| `refactor` | Code restructuring | None | +| `perf` | Performance improvement | None | +| `test` | Adding/fixing tests | None | +| `build` | Build system/deps | None | +| `ci` | CI configuration | None | +| `chore` | Maintenance | None | +| `feat!` / `BREAKING CHANGE:` | Breaking change | Major bump | + +### Scopes + +`core`, `ai`, `genkit`, `openai`, `google-genai`, `anthropic`, `jetty`, `spring`, `firebase`, `localvec`, `mcp`, `samples`, `deps` + +### Examples + +``` +feat(ai): add streaming support for generate +fix(openai): handle rate limit errors gracefully +docs(samples): add RAG example README +feat!: rename Genkit.create() to Genkit.builder() +``` + +--- + +## Error Handling + +```java +public class GenkitException extends RuntimeException { + String errorCode; + Object details; + String traceId; + + public static Builder builder() { ... } +} + +// Throw structured errors +throw GenkitException.builder() + .errorCode("NOT_FOUND") + .message("Model not found: " + modelName) + .build(); +``` + +--- + +## Adding a New Module Checklist + +When adding a new plugin or module: + +1. Create directory under `plugins/{name}/` +2. Add `pom.xml` with parent reference to `genkit-parent` +3. Add module to root `pom.xml` `` section +4. Create package `com.google.genkit.plugins.{name}` +5. Implement `Plugin` interface +6. Create options class with builder pattern +7. Add tests in `src/test/java/` +8. Add `README.md` with setup instructions +9. Add sample app under `samples/{name}/` +10. Format code with `mvn fmt:format` From 2520df33c922cc746b0f591cde7c19a12b479edb Mon Sep 17 00:00:00 2001 From: xavidop Date: Thu, 2 Apr 2026 22:20:03 +0200 Subject: [PATCH 22/50] feat: add google search console --- docs/astro.config.mjs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 725f1830b..99a7af4af 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -8,6 +8,15 @@ export default defineConfig({ starlight({ title: "Java (Unofficial)", favicon: 'favicon.ico', + head: [ + { + tag: "meta", + attrs: { + name: "google-site-verification", + content: "LopxNf0q-1RkccL6rKqWvpaLi8Qcr6HqkWDqyCl8fUA", + }, + }, + ], description: "The Java implementation of the Genkit framework for building AI-powered applications.", social: [ From b3f41ad3340bece4140820ad5834f6dd9b38ce3e Mon Sep 17 00:00:00 2001 From: xavidop Date: Thu, 2 Apr 2026 22:35:13 +0200 Subject: [PATCH 23/50] feat: google analytics --- docs/astro.config.mjs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 99a7af4af..b6497a310 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -16,6 +16,17 @@ export default defineConfig({ content: "LopxNf0q-1RkccL6rKqWvpaLi8Qcr6HqkWDqyCl8fUA", }, }, + { + tag: "script", + attrs: { + async: true, + src: "https://www.googletagmanager.com/gtag/js?id=G-1CT1L0J0RH", + }, + }, + { + tag: "script", + content: `window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'G-1CT1L0J0RH');`, + }, ], description: "The Java implementation of the Genkit framework for building AI-powered applications.", From 4d52b40388c64d54a9b28bd51c33df475bd9ccb4 Mon Sep 17 00:00:00 2001 From: xavidop Date: Fri, 3 Apr 2026 14:13:22 +0200 Subject: [PATCH 24/50] fix: you dont need to call genkit.init() --- samples/mcp/README.md | 2 -- .../google/genkit/samples/MCPServerSample.java | 2 -- .../google/genkit/samples/MiddlewareSample.java | 17 ++++++++--------- .../building-ai-apps-with-genkit-java/SKILL.md | 3 +-- skills/developing-genkit-java/SKILL.md | 8 +++----- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/samples/mcp/README.md b/samples/mcp/README.md index 84fc63d7c..e8fd3242d 100644 --- a/samples/mcp/README.md +++ b/samples/mcp/README.md @@ -199,8 +199,6 @@ genkit.defineTool("my_tool", "My custom tool", return Map.of("result", "processed: " + input.get("input")); }); -genkit.init(); - // Create and start MCP server MCPServer mcpServer = new MCPServer(genkit.getRegistry(), MCPServerOptions.builder() diff --git a/samples/mcp/src/main/java/com/google/genkit/samples/MCPServerSample.java b/samples/mcp/src/main/java/com/google/genkit/samples/MCPServerSample.java index 4f861a52a..a2045c015 100644 --- a/samples/mcp/src/main/java/com/google/genkit/samples/MCPServerSample.java +++ b/samples/mcp/src/main/java/com/google/genkit/samples/MCPServerSample.java @@ -356,8 +356,6 @@ public static void main(String[] args) throws Exception { // ======================================================= // Create and start MCP Server // ======================================================= - // Note: genkit.init() is already called by the builder, so we don't need to - // call it again MCPServerOptions serverOptions = MCPServerOptions.builder().name("genkit-tools-server").version("1.0.0").build(); diff --git a/samples/middleware/src/main/java/com/google/genkit/samples/MiddlewareSample.java b/samples/middleware/src/main/java/com/google/genkit/samples/MiddlewareSample.java index 8d96c2d1d..c6da75e5f 100644 --- a/samples/middleware/src/main/java/com/google/genkit/samples/MiddlewareSample.java +++ b/samples/middleware/src/main/java/com/google/genkit/samples/MiddlewareSample.java @@ -366,9 +366,6 @@ public static void main(String[] args) throws Exception { return sb.toString(); }); - // Initialize Genkit - genkit.init(); - logger.info("\n========================================"); logger.info("Genkit Middleware Sample Started!"); logger.info("========================================\n"); @@ -387,16 +384,18 @@ public static void main(String[] args) throws Exception { logger.info("Reflection server running on http://localhost:3100"); logger.info("\nExample requests:"); logger.info( - " curl -X POST http://localhost:8080/greeting -H 'Content-Type: application/json' -d '\"World\"'"); + " curl -X POST http://localhost:8080/api/flows/greeting -H 'Content-Type: application/json' -d '\"World\"'"); logger.info( - " curl -X POST http://localhost:8080/chat -H 'Content-Type: application/json' -d '\"What is the capital of France?\"'"); + " curl -X POST http://localhost:8080/api/flows/chat -H 'Content-Type: application/json' -d '\"What is the capital of France?\"'"); logger.info( - " curl -X POST http://localhost:8080/fact -H 'Content-Type: application/json' -d '\"penguins\"'"); + " curl -X POST http://localhost:8080/api/flows/fact -H 'Content-Type: application/json' -d '\"penguins\"'"); logger.info( - " curl -X POST http://localhost:8080/joke -H 'Content-Type: application/json' -d '\"programming\"'"); + " curl -X POST http://localhost:8080/api/flows/joke -H 'Content-Type: application/json' -d '\"programming\"'"); logger.info( - " curl -X POST http://localhost:8080/safe -H 'Content-Type: application/json' -d '\"error\"'"); + " curl -X POST http://localhost:8080/api/flows/safe -H 'Content-Type: application/json' -d '\"error\"'"); logger.info( - " curl -X POST http://localhost:8080/metrics -H 'Content-Type: application/json' -d 'null'"); + " curl -X POST http://localhost:8080/api/flows/metrics -H 'Content-Type: application/json' -d 'null'"); + + jetty.start(); } } diff --git a/skills/building-ai-apps-with-genkit-java/SKILL.md b/skills/building-ai-apps-with-genkit-java/SKILL.md index dd1259746..a816471b9 100644 --- a/skills/building-ai-apps-with-genkit-java/SKILL.md +++ b/skills/building-ai-apps-with-genkit-java/SKILL.md @@ -115,7 +115,7 @@ public class MyApp { .prompt(question) .build()).getText()); - genkit.init(); + jetty.start(); } } ``` @@ -831,7 +831,6 @@ public class MyFunction implements HttpFunction { .prompt(input) .build()).getText()); - genkit.init(); this.genkitFunction = OnCallGenkit.fromFlow(genkit, "myFlow"); } diff --git a/skills/developing-genkit-java/SKILL.md b/skills/developing-genkit-java/SKILL.md index 4298ca861..f2a0130d6 100644 --- a/skills/developing-genkit-java/SKILL.md +++ b/skills/developing-genkit-java/SKILL.md @@ -125,14 +125,12 @@ Genkit genkit = Genkit.builder() .plugin(new JettyPlugin()) .build(); -genkit.init(); // Initialize plugins, start servers ``` ### Lifecycle -1. `Genkit.builder()...build()` — creates instance, does NOT initialize -2. `genkit.init()` — calls `plugin.init(registry)` for each plugin, starts dev server if applicable -3. `genkit.stop()` — cleanup resources +1. `Genkit.builder()...build()` — creates instance, and initialize +2. `genkit.stop()` — cleanup resources --- @@ -648,7 +646,7 @@ public class MySample { .build()).getText(); }); - genkit.init(); + jetty.start(); } } ``` From 70ef2cd75c3fb5d839b7a21fe8fea4e407c9e56a Mon Sep 17 00:00:00 2001 From: xavidop Date: Fri, 3 Apr 2026 14:24:57 +0200 Subject: [PATCH 25/50] fix: update prints with correct paths --- .../google/genkit/samples/AwsBedrockSample.java | 10 +++++----- .../google/genkit/samples/AzureFoundrySample.java | 8 ++++---- .../com/google/genkit/samples/DotPromptSample.java | 6 +++--- .../java/com/google/genkit/samples/MCPSample.java | 14 +++++++------- .../samples/postgresql/PostgresRAGSample.java | 6 +++--- .../java/com/google/genkit/samples/RagSample.java | 14 +++++++------- .../genkit/samples/weaviate/WeaviateRAGSample.java | 6 +++--- 7 files changed, 32 insertions(+), 32 deletions(-) diff --git a/samples/aws-bedrock/src/main/java/com/google/genkit/samples/AwsBedrockSample.java b/samples/aws-bedrock/src/main/java/com/google/genkit/samples/AwsBedrockSample.java index f3ab22168..2741e8957 100644 --- a/samples/aws-bedrock/src/main/java/com/google/genkit/samples/AwsBedrockSample.java +++ b/samples/aws-bedrock/src/main/java/com/google/genkit/samples/AwsBedrockSample.java @@ -270,15 +270,15 @@ public static void main(String[] args) throws Exception { System.out.println(""); System.out.println("Example requests:"); System.out.println( - " curl -X POST http://localhost:8080/greeting -H 'Content-Type: application/json' -d '{\"data\": \"World\"}'"); + " curl -X POST http://localhost:8080/api/flows/greeting -H 'Content-Type: application/json' -d '{\"data\": \"World\"}'"); System.out.println( - " curl -X POST http://localhost:8080/tellJoke -H 'Content-Type: application/json' -d '{\"data\": \"cats\"}'"); + " curl -X POST http://localhost:8080/api/flows/tellJoke -H 'Content-Type: application/json' -d '{\"data\": \"cats\"}'"); System.out.println( - " curl -X POST http://localhost:8080/chat -H 'Content-Type: application/json' -d '{\"data\": \"What is AWS Bedrock?\"}'"); + " curl -X POST http://localhost:8080/api/flows/chat -H 'Content-Type: application/json' -d '{\"data\": \"What is AWS Bedrock?\"}'"); System.out.println( - " curl -X POST http://localhost:8080/weatherAssistant -H 'Content-Type: application/json' -d '{\"data\": \"What\\'s the weather in Seattle?\"}'"); + " curl -X POST http://localhost:8080/api/flows/weatherAssistant -H 'Content-Type: application/json' -d '{\"data\": \"What\\'s the weather in Seattle?\"}'"); System.out.println( - " curl -X POST http://localhost:8080/inferenceProfileDemo -H 'Content-Type: application/json' -d '{\"data\": \"Explain quantum computing in simple terms.\"}'"); + " curl -X POST http://localhost:8080/api/flows/inferenceProfileDemo -H 'Content-Type: application/json' -d '{\"data\": \"Explain quantum computing in simple terms.\"}'"); System.out.println(""); System.out.println("Press Ctrl+C to stop."); diff --git a/samples/azure-foundry/src/main/java/com/google/genkit/samples/AzureFoundrySample.java b/samples/azure-foundry/src/main/java/com/google/genkit/samples/AzureFoundrySample.java index 85425f48a..d3f382a39 100644 --- a/samples/azure-foundry/src/main/java/com/google/genkit/samples/AzureFoundrySample.java +++ b/samples/azure-foundry/src/main/java/com/google/genkit/samples/AzureFoundrySample.java @@ -237,13 +237,13 @@ public static void main(String[] args) throws Exception { System.out.println(""); System.out.println("Example requests:"); System.out.println( - " curl -X POST http://localhost:8080/greeting -H 'Content-Type: application/json' -d '{\"data\": \"World\"}'"); + " curl -X POST http://localhost:8080/api/flows/greeting -H 'Content-Type: application/json' -d '{\"data\": \"World\"}'"); System.out.println( - " curl -X POST http://localhost:8080/tellJoke -H 'Content-Type: application/json' -d '{\"data\": \"AI\"}'"); + " curl -X POST http://localhost:8080/api/flows/tellJoke -H 'Content-Type: application/json' -d '{\"data\": \"AI\"}'"); System.out.println( - " curl -X POST http://localhost:8080/chat -H 'Content-Type: application/json' -d '{\"data\": \"What is Azure AI Foundry?\"}'"); + " curl -X POST http://localhost:8080/api/flows/chat -H 'Content-Type: application/json' -d '{\"data\": \"What is Azure AI Foundry?\"}'"); System.out.println( - " curl -X POST http://localhost:8080/weatherAssistant -H 'Content-Type: application/json' -d '{\"data\": \"What\\'s the weather in New York?\"}'"); + " curl -X POST http://localhost:8080/api/flows/weatherAssistant -H 'Content-Type: application/json' -d '{\"data\": \"What\\'s the weather in New York?\"}'"); System.out.println(""); System.out.println("Note: Ensure the models are deployed in your Azure AI Foundry project."); System.out.println("Press Ctrl+C to stop."); diff --git a/samples/dotprompt/src/main/java/com/google/genkit/samples/DotPromptSample.java b/samples/dotprompt/src/main/java/com/google/genkit/samples/DotPromptSample.java index 84aaf86ca..765519819 100644 --- a/samples/dotprompt/src/main/java/com/google/genkit/samples/DotPromptSample.java +++ b/samples/dotprompt/src/main/java/com/google/genkit/samples/DotPromptSample.java @@ -281,15 +281,15 @@ public static void main(String[] args) throws Exception { logger.info(" - reviewCode: Analyze and review code"); logger.info(""); logger.info("Example calls:"); - logger.info(" curl -X POST http://localhost:8080/chefFlow \\"); + logger.info(" curl -X POST http://localhost:8080/api/flows/chefFlow \\"); logger.info(" -H 'Content-Type: application/json' \\"); logger.info(" -d '{\"food\":\"pasta\",\"ingredients\":[\"tomatoes\",\"basil\"]}'"); logger.info(""); - logger.info(" curl -X POST http://localhost:8080/tellStory \\"); + logger.info(" curl -X POST http://localhost:8080/api/flows/tellStory \\"); logger.info(" -H 'Content-Type: application/json' \\"); logger.info(" -d '{\"subject\":\"a brave knight\",\"personality\":\"dramatic\"}'"); logger.info(""); - logger.info(" curl -X POST http://localhost:8080/planTrip \\"); + logger.info(" curl -X POST http://localhost:8080/api/flows/planTrip \\"); logger.info(" -H 'Content-Type: application/json' \\"); logger.info(" -d '{\"destination\":\"Tokyo\",\"duration\":5,\"budget\":\"$3000\",\"interests\":[\"food\",\"culture\"]}'"); logger.info(""); diff --git a/samples/mcp/src/main/java/com/google/genkit/samples/MCPSample.java b/samples/mcp/src/main/java/com/google/genkit/samples/MCPSample.java index 7f9afa3c1..3b3c9a474 100644 --- a/samples/mcp/src/main/java/com/google/genkit/samples/MCPSample.java +++ b/samples/mcp/src/main/java/com/google/genkit/samples/MCPSample.java @@ -367,19 +367,19 @@ public static void main(String[] args) throws Exception { logger.info("Reflection server running on http://localhost:3100"); logger.info("\nExample requests:"); logger.info( - " curl -X POST http://localhost:8080/listMcpTools -H 'Content-Type: application/json' -d 'null'"); + " curl -X POST http://localhost:8080/api/flows/listMcpTools -H 'Content-Type: application/json' -d 'null'"); logger.info( - " curl -X POST http://localhost:8080/fileAssistant -H 'Content-Type: application/json' -d '\"List files in the temp directory\"'"); + " curl -X POST http://localhost:8080/api/flows/fileAssistant -H 'Content-Type: application/json' -d '\"List files in the temp directory\"'"); logger.info( - " curl -X POST http://localhost:8080/readFile -H 'Content-Type: application/json' -d '\"/tmp/test.txt\"'"); + " curl -X POST http://localhost:8080/api/flows/readFile -H 'Content-Type: application/json' -d '\"/tmp/test.txt\"'"); logger.info( - " curl -X POST http://localhost:8080/listResources -H 'Content-Type: application/json' -d '\"filesystem\"'"); + " curl -X POST http://localhost:8080/api/flows/listResources -H 'Content-Type: application/json' -d '\"filesystem\"'"); logger.info( - " curl -X POST http://localhost:8080/toolExplorer -H 'Content-Type: application/json' -d '\"Generate a random number\"'"); + " curl -X POST http://localhost:8080/api/flows/toolExplorer -H 'Content-Type: application/json' -d '\"Generate a random number\"'"); logger.info( - " curl -X POST http://localhost:8080/mcpStatus -H 'Content-Type: application/json' -d 'null'"); + " curl -X POST http://localhost:8080/api/flows/mcpStatus -H 'Content-Type: application/json' -d 'null'"); logger.info( - " curl -X POST http://localhost:8080/writeReadDemo -H 'Content-Type: application/json' -d '\"Hello from Genkit MCP!\"'"); + " curl -X POST http://localhost:8080/api/flows/writeReadDemo -H 'Content-Type: application/json' -d '\"Hello from Genkit MCP!\"'"); // Add shutdown hook to cleanup MCP connections Runtime.getRuntime() diff --git a/samples/postgresql/src/main/java/com/google/genkit/samples/postgresql/PostgresRAGSample.java b/samples/postgresql/src/main/java/com/google/genkit/samples/postgresql/PostgresRAGSample.java index 2e2c895ad..8d562f79c 100644 --- a/samples/postgresql/src/main/java/com/google/genkit/samples/postgresql/PostgresRAGSample.java +++ b/samples/postgresql/src/main/java/com/google/genkit/samples/postgresql/PostgresRAGSample.java @@ -216,11 +216,11 @@ public static void main(String[] args) { logger.info(""); logger.info("Example usage:"); logger.info( - " curl -X POST http://localhost:4000/api/flows/indexDocuments -H 'Content-Type: application/json' -d '{}'"); + " curl -X POST http://localhost:8081/api/flows/indexDocuments -H 'Content-Type: application/json' -d '{}'"); logger.info( - " curl -X POST http://localhost:4000/api/flows/retrieveDocuments -H 'Content-Type: application/json' -d '{\"data\": \"sci-fi movie\"}'"); + " curl -X POST http://localhost:8081/api/flows/retrieveDocuments -H 'Content-Type: application/json' -d '{\"data\": \"sci-fi movie\"}'"); logger.info( - " curl -X POST http://localhost:4000/api/flows/ragQuery -H 'Content-Type: application/json' -d '{\"data\": \"What Christopher Nolan films are mentioned?\"}'"); + " curl -X POST http://localhost:8081/api/flows/ragQuery -H 'Content-Type: application/json' -d '{\"data\": \"What Christopher Nolan films are mentioned?\"}'"); logger.info("=".repeat(60)); // Keep the application running diff --git a/samples/rag/src/main/java/com/google/genkit/samples/RagSample.java b/samples/rag/src/main/java/com/google/genkit/samples/RagSample.java index a1585e838..5b5d1777b 100644 --- a/samples/rag/src/main/java/com/google/genkit/samples/RagSample.java +++ b/samples/rag/src/main/java/com/google/genkit/samples/RagSample.java @@ -271,25 +271,25 @@ public static void main(String[] args) throws Exception { logger.info("Example calls:"); logger.info(""); logger.info("1. First, index the data:"); - logger.info(" curl -X POST http://localhost:8080/indexWorldCapitals"); - logger.info(" curl -X POST http://localhost:8080/indexDogBreeds"); - logger.info(" curl -X POST http://localhost:8080/indexCoffeeFacts"); + logger.info(" curl -X POST http://localhost:8080/api/flows/indexWorldCapitals"); + logger.info(" curl -X POST http://localhost:8080/api/flows/indexDogBreeds"); + logger.info(" curl -X POST http://localhost:8080/api/flows/indexCoffeeFacts"); logger.info(""); logger.info("2. Then query:"); - logger.info(" curl -X POST http://localhost:8080/askAboutCapitals \\"); + logger.info(" curl -X POST http://localhost:8080/api/flows/askAboutCapitals \\"); logger.info(" -H 'Content-Type: application/json' \\"); logger.info(" -d '\"What is the capital of France?\"'"); logger.info(""); - logger.info(" curl -X POST http://localhost:8080/askAboutDogs \\"); + logger.info(" curl -X POST http://localhost:8080/api/flows/askAboutDogs \\"); logger.info(" -H 'Content-Type: application/json' \\"); logger.info(" -d '\"What are good family dogs?\"'"); logger.info(""); - logger.info(" curl -X POST http://localhost:8080/askAboutCoffee \\"); + logger.info(" curl -X POST http://localhost:8080/api/flows/askAboutCoffee \\"); logger.info(" -H 'Content-Type: application/json' \\"); logger.info(" -d '\"How is espresso made?\"'"); logger.info(""); logger.info("3. Retrieve without generation:"); - logger.info(" curl -X POST http://localhost:8080/retrieveDocuments \\"); + logger.info(" curl -X POST http://localhost:8080/api/flows/retrieveDocuments \\"); logger.info(" -H 'Content-Type: application/json' \\"); logger.info(" -d '{\"query\":\"France\",\"store\":\"world-capitals\",\"k\":2}'"); logger.info(""); diff --git a/samples/weaviate/src/main/java/com/google/genkit/samples/weaviate/WeaviateRAGSample.java b/samples/weaviate/src/main/java/com/google/genkit/samples/weaviate/WeaviateRAGSample.java index 89900ae6e..6c1a89ac7 100644 --- a/samples/weaviate/src/main/java/com/google/genkit/samples/weaviate/WeaviateRAGSample.java +++ b/samples/weaviate/src/main/java/com/google/genkit/samples/weaviate/WeaviateRAGSample.java @@ -214,11 +214,11 @@ public static void main(String[] args) { logger.info(""); logger.info("Example usage:"); logger.info( - " curl -X POST http://localhost:4000/api/flows/indexDocuments -H 'Content-Type: application/json' -d '{}'"); + " curl -X POST http://localhost:8081/api/flows/indexDocuments -H 'Content-Type: application/json' -d '{}'"); logger.info( - " curl -X POST http://localhost:4000/api/flows/retrieveDocuments -H 'Content-Type: application/json' -d '{\"data\": \"sci-fi movie\"}'"); + " curl -X POST http://localhost:8081/api/flows/retrieveDocuments -H 'Content-Type: application/json' -d '{\"data\": \"sci-fi movie\"}'"); logger.info( - " curl -X POST http://localhost:4000/api/flows/ragQuery -H 'Content-Type: application/json' -d '{\"data\": \"What Christopher Nolan films are mentioned?\"}'"); + " curl -X POST http://localhost:8081/api/flows/ragQuery -H 'Content-Type: application/json' -d '{\"data\": \"What Christopher Nolan films are mentioned?\"}'"); logger.info("=".repeat(60)); // Keep the application running From 6a7ceaf71b0821cfebf2c4eafe9c841578576017 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 Apr 2026 07:59:19 +0000 Subject: [PATCH 26/50] chore(deps): bump defu Bumps the npm_and_yarn group with 1 update in the /docs directory: [defu](https://github.com/unjs/defu). Updates `defu` from 6.1.4 to 6.1.6 - [Release notes](https://github.com/unjs/defu/releases) - [Changelog](https://github.com/unjs/defu/blob/main/CHANGELOG.md) - [Commits](https://github.com/unjs/defu/compare/v6.1.4...v6.1.6) --- updated-dependencies: - dependency-name: defu dependency-version: 6.1.6 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- docs/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 7c3ac67b0..8beb957f0 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -2929,9 +2929,9 @@ } }, "node_modules/defu": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.6.tgz", + "integrity": "sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug==", "license": "MIT" }, "node_modules/dequal": { From 7837585721b11ca407b5af1b3eb6d28a3c43eafb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 09:27:03 +0000 Subject: [PATCH 27/50] chore(deps)(deps): bump io.pinecone:pinecone-client from 6.1.0 to 6.2.0 Bumps [io.pinecone:pinecone-client](https://github.com/pinecone-io/pinecone-java-client) from 6.1.0 to 6.2.0. - [Release notes](https://github.com/pinecone-io/pinecone-java-client/releases) - [Changelog](https://github.com/pinecone-io/pinecone-java-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/pinecone-io/pinecone-java-client/compare/v6.1.0...v6.2.0) --- updated-dependencies: - dependency-name: io.pinecone:pinecone-client dependency-version: 6.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/pinecone/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/pinecone/pom.xml b/plugins/pinecone/pom.xml index b553f5590..e3da8bcd5 100644 --- a/plugins/pinecone/pom.xml +++ b/plugins/pinecone/pom.xml @@ -36,7 +36,7 @@ false - 6.1.0 + 6.2.0 From dd62f811710443ff07ca47eb18fc6dd00c4a5909 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 09:26:38 +0000 Subject: [PATCH 28/50] chore(deps)(deps): bump aws.sdk.version from 2.42.23 to 2.42.28 Bumps `aws.sdk.version` from 2.42.23 to 2.42.28. Updates `software.amazon.awssdk:auth` from 2.42.23 to 2.42.28 Updates `software.amazon.awssdk:http-client-spi` from 2.42.23 to 2.42.28 Updates `software.amazon.awssdk:regions` from 2.42.23 to 2.42.28 --- updated-dependencies: - dependency-name: software.amazon.awssdk:auth dependency-version: 2.42.28 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.42.28 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:regions dependency-version: 2.42.28 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- plugins/aws-bedrock/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/aws-bedrock/pom.xml b/plugins/aws-bedrock/pom.xml index 1b4182401..b37cd9fb9 100644 --- a/plugins/aws-bedrock/pom.xml +++ b/plugins/aws-bedrock/pom.xml @@ -36,7 +36,7 @@ false - 2.42.23 + 2.42.28 From e66193eca97227f1d838cc5b970ef02281db2531 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 09:26:19 +0000 Subject: [PATCH 29/50] chore(ci): bump actions/upload-pages-artifact from 3 to 4 Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 3 to 4. - [Release notes](https://github.com/actions/upload-pages-artifact/releases) - [Commits](https://github.com/actions/upload-pages-artifact/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/upload-pages-artifact dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 56b5415ce..1fd673048 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -72,7 +72,7 @@ jobs: cp -r target/site/apidocs/* docs/dist/javadoc/ 2>/dev/null || true - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v4 with: path: docs/dist From bbc30f7d8f613e939a0e8d3a05cdc536b3f420eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 09:26:17 +0000 Subject: [PATCH 30/50] chore(ci): bump actions/setup-node from 4 to 6 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 6. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 1fd673048..dc2a1f690 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -47,7 +47,7 @@ jobs: cache: "maven" - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: "20" cache: "npm" From cedab1dded2fcff181b754ecdd4dc73c17d761db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 09:26:12 +0000 Subject: [PATCH 31/50] chore(ci): bump actions/deploy-pages from 4 to 5 Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages) from 4 to 5. - [Release notes](https://github.com/actions/deploy-pages/releases) - [Commits](https://github.com/actions/deploy-pages/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/deploy-pages dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index dc2a1f690..8a8005d29 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -85,4 +85,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 From 5e5ce6472651f7c877f5f8313c8631eb3567b2a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 09:26:09 +0000 Subject: [PATCH 32/50] chore(deps)(deps): bump com.google.genai:google-genai Bumps [com.google.genai:google-genai](https://github.com/googleapis/java-genai) from 1.45.0 to 1.46.0. - [Release notes](https://github.com/googleapis/java-genai/releases) - [Changelog](https://github.com/googleapis/java-genai/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-genai/compare/v1.45.0...v1.46.0) --- updated-dependencies: - dependency-name: com.google.genai:google-genai dependency-version: 1.46.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/google-genai/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/google-genai/pom.xml b/plugins/google-genai/pom.xml index b66d5da6b..c944eaa53 100644 --- a/plugins/google-genai/pom.xml +++ b/plugins/google-genai/pom.xml @@ -49,7 +49,7 @@ com.google.genai google-genai - 1.45.0 + 1.46.0 From 7bc87e139059388fb6015ae4ef1c72b76e6f1b62 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 09:26:09 +0000 Subject: [PATCH 33/50] chore(ci): bump actions/checkout from 4 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 8a8005d29..ac447a579 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -37,7 +37,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up JDK 21 uses: actions/setup-java@v4 From af000c693844ff365946eddcc3ba01028cfaaf52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 09:26:01 +0000 Subject: [PATCH 34/50] chore(ci): bump actions/setup-java from 4 to 5 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 5. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index ac447a579..b30062c8d 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -40,7 +40,7 @@ jobs: uses: actions/checkout@v6 - name: Set up JDK 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: "21" distribution: "temurin" From ccb61884c2e60791a70dd78506f749c5daf3ccca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 05:03:52 +0000 Subject: [PATCH 35/50] chore(deps): bump vite Bumps the npm_and_yarn group with 1 update in the /docs directory: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `vite` from 6.4.1 to 6.4.2 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v6.4.2/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v6.4.2/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 6.4.2 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- docs/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 8beb957f0..23f154d07 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -6613,9 +6613,9 @@ } }, "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", From 0583a0f58967b0740e41a69fd1550b7ff5b5a1ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 09:44:42 +0000 Subject: [PATCH 36/50] chore(deps)(deps): bump opentelemetry.version from 1.60.1 to 1.61.0 Bumps `opentelemetry.version` from 1.60.1 to 1.61.0. Updates `io.opentelemetry:opentelemetry-api` from 1.60.1 to 1.61.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-java/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-java/compare/v1.60.1...v1.61.0) Updates `io.opentelemetry:opentelemetry-sdk` from 1.60.1 to 1.61.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-java/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-java/compare/v1.60.1...v1.61.0) Updates `io.opentelemetry:opentelemetry-sdk-trace` from 1.60.1 to 1.61.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-java/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-java/compare/v1.60.1...v1.61.0) Updates `io.opentelemetry:opentelemetry-sdk-metrics` from 1.60.1 to 1.61.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-java/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-java/compare/v1.60.1...v1.61.0) Updates `io.opentelemetry:opentelemetry-exporter-otlp` from 1.60.1 to 1.61.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-java/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-java/compare/v1.60.1...v1.61.0) --- updated-dependencies: - dependency-name: io.opentelemetry:opentelemetry-api dependency-version: 1.61.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: io.opentelemetry:opentelemetry-sdk dependency-version: 1.61.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: io.opentelemetry:opentelemetry-sdk-trace dependency-version: 1.61.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: io.opentelemetry:opentelemetry-sdk-metrics dependency-version: 1.61.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: io.opentelemetry:opentelemetry-exporter-otlp dependency-version: 1.61.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6ad171b59..de25ae4f3 100644 --- a/pom.xml +++ b/pom.xml @@ -134,7 +134,7 @@ 2.0.17 1.5.32 12.1.6 - 1.60.1 + 1.61.0 6.0.3 5.23.0 4.38.0 From 733485fc8eb692f292221a9ea24aca6ee2ba9f81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 09:45:59 +0000 Subject: [PATCH 37/50] chore(deps)(deps): bump com.google.genai:google-genai Bumps [com.google.genai:google-genai](https://github.com/googleapis/java-genai) from 1.46.0 to 1.47.0. - [Release notes](https://github.com/googleapis/java-genai/releases) - [Changelog](https://github.com/googleapis/java-genai/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-genai/compare/v1.46.0...v1.47.0) --- updated-dependencies: - dependency-name: com.google.genai:google-genai dependency-version: 1.47.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/google-genai/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/google-genai/pom.xml b/plugins/google-genai/pom.xml index c944eaa53..cc8bbfb11 100644 --- a/plugins/google-genai/pom.xml +++ b/plugins/google-genai/pom.xml @@ -49,7 +49,7 @@ com.google.genai google-genai - 1.46.0 + 1.47.0 From 247120fc8d25645730367551e79e34f820753711 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 09:45:38 +0000 Subject: [PATCH 38/50] chore(deps)(deps): bump aws.sdk.version from 2.42.28 to 2.42.33 Bumps `aws.sdk.version` from 2.42.28 to 2.42.33. Updates `software.amazon.awssdk:auth` from 2.42.28 to 2.42.33 Updates `software.amazon.awssdk:http-client-spi` from 2.42.28 to 2.42.33 Updates `software.amazon.awssdk:regions` from 2.42.28 to 2.42.33 --- updated-dependencies: - dependency-name: software.amazon.awssdk:auth dependency-version: 2.42.33 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:http-client-spi dependency-version: 2.42.33 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: software.amazon.awssdk:regions dependency-version: 2.42.33 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- plugins/aws-bedrock/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/aws-bedrock/pom.xml b/plugins/aws-bedrock/pom.xml index b37cd9fb9..42b23a71a 100644 --- a/plugins/aws-bedrock/pom.xml +++ b/plugins/aws-bedrock/pom.xml @@ -36,7 +36,7 @@ false - 2.42.28 + 2.42.33 From de8fb11a8bdac642b0364d3b6228337e0d726ced Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 09:45:18 +0000 Subject: [PATCH 39/50] chore(deps)(deps): bump com.google.cloud:google-cloud-logging Bumps [com.google.cloud:google-cloud-logging](https://github.com/googleapis/java-logging) from 3.29.0 to 3.31.0. - [Release notes](https://github.com/googleapis/java-logging/releases) - [Changelog](https://github.com/googleapis/java-logging/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/java-logging/commits) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-logging dependency-version: 3.31.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/firebase/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/firebase/pom.xml b/plugins/firebase/pom.xml index cbcff0ef5..d6dfdf57b 100644 --- a/plugins/firebase/pom.xml +++ b/plugins/firebase/pom.xml @@ -38,7 +38,7 @@ false 9.8.0 3.39.0 - 3.29.0 + 3.31.0 2.88.0 3.89.0 2.0.0 From f0b9b1c5562ac21e1690c6d00b547f8e15ae5b76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 09:45:08 +0000 Subject: [PATCH 40/50] chore(deps)(deps): bump com.google.cloud.functions:functions-framework-api Bumps [com.google.cloud.functions:functions-framework-api](https://github.com/GoogleCloudPlatform/functions-framework-java) from 2.0.0 to 2.0.1. - [Release notes](https://github.com/GoogleCloudPlatform/functions-framework-java/releases) - [Commits](https://github.com/GoogleCloudPlatform/functions-framework-java/compare/java-function-invoker-v2.0.0...java-function-invoker-v2.0.1) --- updated-dependencies: - dependency-name: com.google.cloud.functions:functions-framework-api dependency-version: 2.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- plugins/firebase/pom.xml | 2 +- samples/firebase/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/firebase/pom.xml b/plugins/firebase/pom.xml index d6dfdf57b..d340bbc31 100644 --- a/plugins/firebase/pom.xml +++ b/plugins/firebase/pom.xml @@ -41,7 +41,7 @@ 3.31.0 2.88.0 3.89.0 - 2.0.0 + 2.0.1 0.36.0 diff --git a/samples/firebase/pom.xml b/samples/firebase/pom.xml index 6b7617abf..250ce897c 100644 --- a/samples/firebase/pom.xml +++ b/samples/firebase/pom.xml @@ -42,7 +42,7 @@ 1.0.0-SNAPSHOT true true - 2.0.0 + 2.0.1 From 26893b3d5027d705b47eed410879b56d40d4d465 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 09:45:03 +0000 Subject: [PATCH 41/50] chore(deps)(deps-dev): bump com.google.cloud.functions:function-maven-plugin Bumps [com.google.cloud.functions:function-maven-plugin](https://github.com/GoogleCloudPlatform/functions-framework-java) from 1.0.0 to 1.0.1. - [Release notes](https://github.com/GoogleCloudPlatform/functions-framework-java/releases) - [Commits](https://github.com/GoogleCloudPlatform/functions-framework-java/compare/function-maven-plugin-v1.0.0...function-maven-plugin-v1.0.1) --- updated-dependencies: - dependency-name: com.google.cloud.functions:function-maven-plugin dependency-version: 1.0.1 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- samples/firebase/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/firebase/pom.xml b/samples/firebase/pom.xml index 250ce897c..c45418809 100644 --- a/samples/firebase/pom.xml +++ b/samples/firebase/pom.xml @@ -107,7 +107,7 @@ com.google.cloud.functions function-maven-plugin - 1.0.0 + 1.0.1 com.google.genkit.samples.firebase.functions.GeneratePoemFunction From e7b1ea52df7ef95dcd7ffebe6401ebc04c72a64d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 10:25:34 +0000 Subject: [PATCH 42/50] chore(deps)(deps): bump com.google.cloud:google-cloud-trace Bumps [com.google.cloud:google-cloud-trace](https://github.com/googleapis/google-cloud-java) from 2.88.0 to 2.90.0. - [Release notes](https://github.com/googleapis/google-cloud-java/releases) - [Changelog](https://github.com/googleapis/google-cloud-java/blob/main/java-document-ai/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-cloud-java/commits) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-trace dependency-version: 2.90.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/firebase/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/firebase/pom.xml b/plugins/firebase/pom.xml index d340bbc31..c777d5dea 100644 --- a/plugins/firebase/pom.xml +++ b/plugins/firebase/pom.xml @@ -39,7 +39,7 @@ 9.8.0 3.39.0 3.31.0 - 2.88.0 + 2.90.0 3.89.0 2.0.1 0.36.0 From d1722c0bbbe753a82612c683c73851462941c974 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 10:33:51 +0000 Subject: [PATCH 43/50] chore(deps)(deps): bump com.google.cloud:google-cloud-monitoring Bumps [com.google.cloud:google-cloud-monitoring](https://github.com/googleapis/google-cloud-java) from 3.89.0 to 3.91.0. - [Release notes](https://github.com/googleapis/google-cloud-java/releases) - [Changelog](https://github.com/googleapis/google-cloud-java/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/google-cloud-java/commits) --- updated-dependencies: - dependency-name: com.google.cloud:google-cloud-monitoring dependency-version: 3.91.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- plugins/firebase/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/firebase/pom.xml b/plugins/firebase/pom.xml index c777d5dea..167453900 100644 --- a/plugins/firebase/pom.xml +++ b/plugins/firebase/pom.xml @@ -40,7 +40,7 @@ 3.39.0 3.31.0 2.90.0 - 3.89.0 + 3.91.0 2.0.1 0.36.0 From 97cbb24d31ce2aeba4538c5ce2fa543c9bc3b146 Mon Sep 17 00:00:00 2001 From: xavidop Date: Mon, 30 Mar 2026 01:50:29 +0200 Subject: [PATCH 44/50] fix: V2 feedback --- .../com/google/genkit/ReflectionServerV2.java | 89 +++++++++++++------ 1 file changed, 62 insertions(+), 27 deletions(-) diff --git a/genkit/src/main/java/com/google/genkit/ReflectionServerV2.java b/genkit/src/main/java/com/google/genkit/ReflectionServerV2.java index f6a8e2a4a..10b4de438 100644 --- a/genkit/src/main/java/com/google/genkit/ReflectionServerV2.java +++ b/genkit/src/main/java/com/google/genkit/ReflectionServerV2.java @@ -23,7 +23,6 @@ import com.google.genkit.core.Action; import com.google.genkit.core.ActionContext; import com.google.genkit.core.ActionDesc; -import com.google.genkit.core.ActionRunResult; import com.google.genkit.core.JsonUtils; import com.google.genkit.core.Registry; import com.google.genkit.core.tracing.Tracer; @@ -76,6 +75,8 @@ public class ReflectionServerV2 { private final ConcurrentHashMap> pendingRequests = new ConcurrentHashMap<>(); + + /** Maps traceId → Thread for targeted cancellation of running actions. */ private final ConcurrentHashMap activeActions = new ConcurrentHashMap<>(); private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); @@ -413,27 +414,66 @@ private void handleRunAction(String requestId, JsonNode params) { return; } - // Track this action for cancellation - activeActions.put(requestId, Thread.currentThread()); + // Build stream callback if streaming is requested + java.util.function.Consumer streamCallback = null; + if (stream) { + streamCallback = + (chunk) -> { + Map chunkParams = new HashMap<>(); + chunkParams.put("requestId", requestId); + chunkParams.put("chunk", chunk); + sendNotification("streamChunk", chunkParams); + }; + } ActionContext context = new ActionContext(registry); - ActionRunResult result = action.runJsonWithTelemetry(context, input, null); - - traceId = result.getTraceId(); - // Send runActionState notification with traceId - if (traceId != null) { - Map stateParams = new HashMap<>(); - stateParams.put("requestId", requestId); - Map state = new HashMap<>(); - state.put("traceId", traceId); - stateParams.put("state", state); - sendNotification("runActionState", stateParams); + // Create the telemetry span manually so we can get the traceId + // BEFORE the action starts producing stream chunks. + // This ensures runActionState is sent before any streamChunk notifications. + ActionDesc desc = action.getDesc(); + com.google.genkit.core.tracing.SpanMetadata spanMetadata = + com.google.genkit.core.tracing.SpanMetadata.builder() + .name(desc.getName()) + .type(desc.getType().getValue()) + .build(); + + final java.util.function.Consumer finalStreamCallback = streamCallback; + JsonNode jsonResult = + Tracer.runInNewSpan( + context, + spanMetadata, + input, + (spanCtx, in) -> { + // Capture traceId and send state notification before action runs + String spanTraceId = spanCtx.getTraceId(); + if (spanTraceId != null) { + activeActions.put(spanTraceId, Thread.currentThread()); + + Map stateParams = new HashMap<>(); + stateParams.put("requestId", requestId); + Map state = new HashMap<>(); + state.put("traceId", spanTraceId); + stateParams.put("state", state); + sendNotification("runActionState", stateParams); + } + + // Now run the action - stream chunks will be sent AFTER traceId + return action.runJson( + context.withSpanContext(spanCtx), in, finalStreamCallback); + }); + + // Get traceId that was stored in activeActions during span execution + for (Map.Entry entry : activeActions.entrySet()) { + if (entry.getValue() == Thread.currentThread()) { + traceId = entry.getKey(); + break; + } } // Send final result Map responseResult = new HashMap<>(); - responseResult.put("result", result.getResult()); + responseResult.put("result", jsonResult); if (traceId != null) { Map telemetry = new HashMap<>(); telemetry.put("traceId", traceId); @@ -462,7 +502,9 @@ private void handleRunAction(String requestId, JsonNode params) { isInterrupt ? "Action was cancelled" : e.getMessage(), errorData); } finally { - activeActions.remove(requestId); + if (traceId != null) { + activeActions.remove(traceId); + } } }); } @@ -491,17 +533,10 @@ private void handleCancelAction(String requestId, JsonNode params) { String traceId = params.get("traceId").asText(); - // Look through active actions - find by traceId is not straightforward since - // activeActions is keyed by requestId. We interrupt all matching threads. - boolean found = false; - for (Map.Entry entry : activeActions.entrySet()) { - // Interrupt the thread to cancel the action - entry.getValue().interrupt(); - activeActions.remove(entry.getKey()); - found = true; - } - - if (found) { + // Look up the specific action thread by traceId + Thread actionThread = activeActions.remove(traceId); + if (actionThread != null) { + actionThread.interrupt(); sendResponse(requestId, Map.of("message", "Action cancelled")); } else { sendError(requestId, 404, "Action not found or already completed", null); From 11d70c39fce4c0d0592a375237fa6c8d8b128d2b Mon Sep 17 00:00:00 2001 From: xavidop Date: Mon, 13 Apr 2026 16:55:23 +0200 Subject: [PATCH 45/50] fix: ffedback and refactored the code to work with both --- .../main/java/com/google/genkit/Genkit.java | 4 +- .../java/com/google/genkit/GenkitOptions.java | 17 ++++++ .../com/google/genkit/ReflectionServer.java | 5 +- .../com/google/genkit/ReflectionServerV2.java | 5 +- .../com/google/genkit/RuntimeIdGenerator.java | 57 +++++++++++++++++++ 5 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 genkit/src/main/java/com/google/genkit/RuntimeIdGenerator.java diff --git a/genkit/src/main/java/com/google/genkit/Genkit.java b/genkit/src/main/java/com/google/genkit/Genkit.java index 73dd4d6ff..3964a3fb4 100644 --- a/genkit/src/main/java/com/google/genkit/Genkit.java +++ b/genkit/src/main/java/com/google/genkit/Genkit.java @@ -1763,7 +1763,7 @@ private void startReflectionServer() { private void startReflectionServerV1() { try { int port = options.getReflectionPort(); - reflectionServer = new ReflectionServer(registry, port); + reflectionServer = new ReflectionServer(registry, port, options.getName()); reflectionServer.start(); logger.info("Reflection server started on port {}", port); @@ -1777,7 +1777,7 @@ private void startReflectionServerV1() { private void startReflectionServerV2(String serverUrl) { try { - reflectionServerV2 = new ReflectionServerV2(registry, serverUrl); + reflectionServerV2 = new ReflectionServerV2(registry, serverUrl, options.getName()); reflectionServerV2.start(); logger.info("Reflection V2 client connecting to {}", serverUrl); } catch (Exception e) { diff --git a/genkit/src/main/java/com/google/genkit/GenkitOptions.java b/genkit/src/main/java/com/google/genkit/GenkitOptions.java index 2f2ac9353..2c40e4a54 100644 --- a/genkit/src/main/java/com/google/genkit/GenkitOptions.java +++ b/genkit/src/main/java/com/google/genkit/GenkitOptions.java @@ -25,12 +25,14 @@ public class GenkitOptions { private final int reflectionPort; private final String projectRoot; private final String promptDir; + private final String name; private GenkitOptions(Builder builder) { this.devMode = builder.devMode; this.reflectionPort = builder.reflectionPort; this.projectRoot = builder.projectRoot; this.promptDir = builder.promptDir; + this.name = builder.name; } /** @@ -79,12 +81,22 @@ public String getPromptDir() { return promptDir; } + /** + * Returns the optional instance name for this Genkit instance. + * + * @return the instance name, or null if not set + */ + public String getName() { + return name; + } + /** Builder for GenkitOptions. */ public static class Builder { private boolean devMode = isDevModeFromEnv(); private int reflectionPort = getReflectionPortFromEnv(); private String projectRoot = System.getProperty("user.dir"); private String promptDir = "/prompts"; + private String name; private static boolean isDevModeFromEnv() { String env = System.getenv("GENKIT_ENV"); @@ -123,6 +135,11 @@ public Builder promptDir(String promptDir) { return this; } + public Builder name(String name) { + this.name = name; + return this; + } + public GenkitOptions build() { return new GenkitOptions(this); } diff --git a/genkit/src/main/java/com/google/genkit/ReflectionServer.java b/genkit/src/main/java/com/google/genkit/ReflectionServer.java index f2d297506..01b8a7a90 100644 --- a/genkit/src/main/java/com/google/genkit/ReflectionServer.java +++ b/genkit/src/main/java/com/google/genkit/ReflectionServer.java @@ -73,11 +73,12 @@ public class ReflectionServer { * * @param registry the Genkit registry * @param port the port to listen on + * @param instanceName optional instance name for the runtime ID (may be null) */ - public ReflectionServer(Registry registry, int port) { + public ReflectionServer(Registry registry, int port, String instanceName) { this.registry = registry; this.port = port; - this.runtimeId = "java-" + ProcessHandle.current().pid() + "-" + System.currentTimeMillis(); + this.runtimeId = RuntimeIdGenerator.generate(instanceName); this.evaluationManager = new EvaluationManager(registry); // Register local telemetry store for Dev UI trace access diff --git a/genkit/src/main/java/com/google/genkit/ReflectionServerV2.java b/genkit/src/main/java/com/google/genkit/ReflectionServerV2.java index 10b4de438..7f8a20058 100644 --- a/genkit/src/main/java/com/google/genkit/ReflectionServerV2.java +++ b/genkit/src/main/java/com/google/genkit/ReflectionServerV2.java @@ -90,11 +90,12 @@ public class ReflectionServerV2 { * * @param registry the Genkit registry * @param serverUrl the WebSocket server URL (e.g., ws://localhost:4100) + * @param instanceName optional instance name for the runtime ID (may be null) */ - public ReflectionServerV2(Registry registry, String serverUrl) { + public ReflectionServerV2(Registry registry, String serverUrl, String instanceName) { this.registry = registry; this.serverUrl = serverUrl; - this.runtimeId = ProcessHandle.current().pid() + ""; + this.runtimeId = RuntimeIdGenerator.generate(instanceName); this.httpClient = HttpClient.newHttpClient(); // Register local telemetry store for Dev UI trace access diff --git a/genkit/src/main/java/com/google/genkit/RuntimeIdGenerator.java b/genkit/src/main/java/com/google/genkit/RuntimeIdGenerator.java new file mode 100644 index 000000000..0539414a0 --- /dev/null +++ b/genkit/src/main/java/com/google/genkit/RuntimeIdGenerator.java @@ -0,0 +1,57 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.genkit; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Generates globally unique runtime IDs for Genkit instances. + * + *

The ID format is {@code $prefix$suffix} where: + * + *

    + *
  • Prefix: instance name if provided, otherwise the PID + *
  • Suffix: empty for the first instance, {@code _N} for subsequent instances + *
+ * + *

This ensures uniqueness even when multiple Genkit instances exist in the same process. + */ +class RuntimeIdGenerator { + + /** Global counter for Genkit instances, shared across all reflection servers. */ + private static final AtomicInteger instanceCounter = new AtomicInteger(0); + + private RuntimeIdGenerator() {} + + /** + * Generates a globally unique runtime ID. + * + * @param instanceName optional instance name (may be null); if absent, the PID is used as prefix + * @return a unique runtime ID + */ + static String generate(String instanceName) { + String prefix = + (instanceName != null && !instanceName.isEmpty()) + ? instanceName + : String.valueOf(ProcessHandle.current().pid()); + int count = instanceCounter.incrementAndGet(); + String suffix = (count == 1) ? "" : "_" + count; + return prefix + suffix; + } +} From 897749ab0cc395f569ba44fdbbafbad0f668b94a Mon Sep 17 00:00:00 2001 From: xavidop Date: Mon, 13 Apr 2026 17:08:17 +0200 Subject: [PATCH 46/50] fix: refleciton v1 fix --- .../com/google/genkit/RuntimeFileWriter.java | 47 +++++-------------- 1 file changed, 11 insertions(+), 36 deletions(-) diff --git a/genkit/src/main/java/com/google/genkit/RuntimeFileWriter.java b/genkit/src/main/java/com/google/genkit/RuntimeFileWriter.java index 540302ae3..fe1f8524c 100644 --- a/genkit/src/main/java/com/google/genkit/RuntimeFileWriter.java +++ b/genkit/src/main/java/com/google/genkit/RuntimeFileWriter.java @@ -54,48 +54,23 @@ public static void write(int port, String runtimeId) { } /** - * Finds the project root by searching up from the current directory. Prioritizes package.json to - * match the genkit CLI's behavior - this ensures the Java runtime writes to the same .genkit - * directory the CLI reads from. Falls back to pom.xml/build.gradle only if no package.json is - * found. + * Finds the project root by searching up from the current directory. At each level, checks for + * any known project marker file (package.json, pom.xml, build.gradle, etc.). The closest + * directory containing a marker wins, ensuring the runtime file is written next to the project + * that the Genkit CLI detects. * * @return the project root directory path */ private static String findProjectRoot() { Path dir = Paths.get(System.getProperty("user.dir")).toAbsolutePath(); - // First pass: Look for package.json (CLI primary marker) to ensure we match CLI - // behavior - // The CLI looks for package.json first, so we need to find the same root it - // uses - Path cliRoot = null; - Path currentDir = dir; - while (currentDir != null) { - Path packageJson = currentDir.resolve("package.json"); - if (Files.exists(packageJson)) { - cliRoot = currentDir; - logger.debug("Found CLI project root at: {} (found package.json)", currentDir); - break; - } - Path parent = currentDir.getParent(); - if (parent == null || parent.equals(currentDir)) { - break; - } - currentDir = parent; - } - - // If we found a package.json (CLI root), use that - if (cliRoot != null) { - return cliRoot.toString(); - } - - // Second pass: Fall back to Java/other markers if no package.json found - String[] fallbackMarkers = { - "pom.xml", "build.gradle", "go.mod", "pyproject.toml", "requirements.txt" + String[] markers = { + "package.json", "pom.xml", "build.gradle", "go.mod", "pyproject.toml", "requirements.txt" }; - currentDir = dir; + + Path currentDir = dir; while (currentDir != null) { - for (String marker : fallbackMarkers) { + for (String marker : markers) { Path markerFile = currentDir.resolve(marker); if (Files.exists(markerFile)) { logger.debug("Found project root at: {} (found {})", currentDir, marker); @@ -105,12 +80,12 @@ private static String findProjectRoot() { Path parent = currentDir.getParent(); if (parent == null || parent.equals(currentDir)) { - logger.warn("Could not find project root, using current directory"); - return System.getProperty("user.dir"); + break; } currentDir = parent; } + logger.warn("Could not find project root, using current directory"); return System.getProperty("user.dir"); } From d95d65c86d8ef6f9f6e035f236bb0852f3dfb8bc Mon Sep 17 00:00:00 2001 From: xavidop Date: Mon, 13 Apr 2026 21:23:11 +0200 Subject: [PATCH 47/50] fix: deps --- samples/firebase/dependency-reduced-pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/firebase/dependency-reduced-pom.xml b/samples/firebase/dependency-reduced-pom.xml index a98fb527c..56d745010 100644 --- a/samples/firebase/dependency-reduced-pom.xml +++ b/samples/firebase/dependency-reduced-pom.xml @@ -27,7 +27,7 @@ com.google.cloud.functions function-maven-plugin - 1.0.0 + 1.0.1 com.google.genkit.samples.firebase.functions.GeneratePoemFunction @@ -68,12 +68,12 @@ com.google.cloud.functions functions-framework-api - 2.0.0 + 2.0.1 provided - 2.0.0 + 2.0.1 21 21 1.0.0-SNAPSHOT From 20f1893208bd8a3a13ffd7867311cd147672eda3 Mon Sep 17 00:00:00 2001 From: xavidop Date: Mon, 13 Apr 2026 21:35:57 +0200 Subject: [PATCH 48/50] fix: license --- docs/src/content.config.ts | 16 ++++++++++++++++ samples/middleware-v2/pom.xml | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/src/content.config.ts b/docs/src/content.config.ts index 7fbcf2c33..3cd166154 100644 --- a/docs/src/content.config.ts +++ b/docs/src/content.config.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import { defineCollection } from "astro:content"; import { docsLoader } from "@astrojs/starlight/loaders"; import { docsSchema } from "@astrojs/starlight/schema"; diff --git a/samples/middleware-v2/pom.xml b/samples/middleware-v2/pom.xml index 0857d4462..a2a1443bb 100644 --- a/samples/middleware-v2/pom.xml +++ b/samples/middleware-v2/pom.xml @@ -42,6 +42,7 @@ 1.0.0-SNAPSHOT true true + com.google.genkit.samples.MiddlewareV2Sample @@ -83,7 +84,7 @@ exec-maven-plugin 3.6.3 - com.google.genkit.samples.MiddlewareV2Sample + ${exec.mainClass} From b88a7d417bc0b386a2dd12db6b2b2e49457eab70 Mon Sep 17 00:00:00 2001 From: xavidop Date: Mon, 13 Apr 2026 21:44:32 +0200 Subject: [PATCH 49/50] fix: continue lifecycle after an interrupts --- .../main/java/com/google/genkit/Genkit.java | 181 ++++++-- .../MiddlewareInterruptRestartSample.java | 385 ++++++++++++++++++ 2 files changed, 527 insertions(+), 39 deletions(-) create mode 100644 samples/middleware-v2/src/main/java/com/google/genkit/samples/MiddlewareInterruptRestartSample.java diff --git a/genkit/src/main/java/com/google/genkit/Genkit.java b/genkit/src/main/java/com/google/genkit/Genkit.java index 3964a3fb4..84a90ecaa 100644 --- a/genkit/src/main/java/com/google/genkit/Genkit.java +++ b/genkit/src/main/java/com/google/genkit/Genkit.java @@ -697,6 +697,13 @@ private ModelResponse generateInternal(GenerateOptions options) throws Genkit request = handleResumeOption(request, options); } + // Extract pending restart requests (handled inside the generate loop for proper middleware + // lifecycle: wrapTool hooks fire for restarted tools, then wrapGenerate fires for next turn) + final List pendingRestarts = new java.util.ArrayList<>(); + if (options.getResume() != null && options.getResume().getRestart() != null) { + pendingRestarts.addAll(options.getResume().getRestart()); + } + // Build model call wrapped with WrapModel hooks ModelNext wrappedModelCall = buildWrappedModelCall(model, options, ctx, middlewares); @@ -713,6 +720,81 @@ private ModelResponse generateInternal(GenerateOptions options) throws Genkit throw new GenkitException("Max tool execution turns (" + maxTurns + ") exceeded"); } + // Handle pending restart tools through middleware before calling model. + // This ensures wrapTool hooks fire for restarted tools, and subsequent + // recursion through generateRef fires wrapGenerate for the next turn. + if (!pendingRestarts.isEmpty()) { + List restarts = new java.util.ArrayList<>(pendingRestarts); + pendingRestarts.clear(); + + // Convert restart requests to tool request parts for middleware execution + List restartParts = + restarts.stream() + .map(Part::toolRequest) + .collect(java.util.stream.Collectors.toList()); + + // Execute through WrapTool chain (fires wrapTool hooks) + ToolExecutionResult toolResult = + executeToolsWithMiddleware(actx, restartParts, allTools, middlewares); + + // If a restart tool interrupts again, fail + if (!toolResult.getInterrupts().isEmpty()) { + throw new GenkitException( + "Tool triggered an interrupt during restart. " + + "Re-interrupting during restart is not supported."); + } + + // Add restart tool responses to messages + List updatedMessages = new java.util.ArrayList<>(req.getMessages()); + + // If last message is a TOOL message (from respond directives), merge restart responses + if (!updatedMessages.isEmpty() + && updatedMessages.get(updatedMessages.size() - 1).getRole() == Role.TOOL) { + Message existingToolMsg = updatedMessages.get(updatedMessages.size() - 1); + List mergedContent = new java.util.ArrayList<>(existingToolMsg.getContent()); + for (Part restartResp : toolResult.getResponses()) { + Map metadata = + restartResp.getMetadata() != null + ? new java.util.HashMap<>(restartResp.getMetadata()) + : new java.util.HashMap<>(); + metadata.put("source", "restart"); + restartResp.setMetadata(metadata); + mergedContent.add(restartResp); + } + existingToolMsg.setContent(mergedContent); + } else { + // Create new TOOL message with restart responses + Message toolResponseMessage = new Message(); + toolResponseMessage.setRole(Role.TOOL); + List restartResponses = new java.util.ArrayList<>(); + for (Part restartResp : toolResult.getResponses()) { + Map metadata = + restartResp.getMetadata() != null + ? new java.util.HashMap<>(restartResp.getMetadata()) + : new java.util.HashMap<>(); + metadata.put("source", "restart"); + restartResp.setMetadata(metadata); + restartResponses.add(restartResp); + } + toolResponseMessage.setContent(restartResponses); + Map toolMsgMetadata = new java.util.HashMap<>(); + toolMsgMetadata.put("resumed", true); + toolResponseMessage.setMetadata(toolMsgMetadata); + updatedMessages.add(toolResponseMessage); + } + + // Recurse through WrapGenerate hooks for the next turn + ModelRequest nextRequest = + ModelRequest.builder() + .messages(updatedMessages) + .config(req.getConfig()) + .tools(req.getTools()) + .output(req.getOutput()) + .build(); + + return generateRef[0].apply(actx, new GenerateParams(nextRequest, turn + 1)); + } + // Call model through WrapModel chain ModelParams mparams = new ModelParams(req, null); ModelResponse response = wrappedModelCall.apply(actx, mparams); @@ -729,7 +811,10 @@ private ModelResponse generateInternal(GenerateOptions options) throws Genkit // If there are interrupts, return immediately if (!toolResult.getInterrupts().isEmpty()) { - return buildInterruptedResponse(response, toolResult); + ModelResponse interruptedResponse = buildInterruptedResponse(response, toolResult); + // Set original request so getMessages() includes conversation history + interruptedResponse.setRequest(req); + return interruptedResponse; } // Build next request with updated messages @@ -967,9 +1052,16 @@ private ModelRequest handleResumeOption(ModelRequest request, GenerateOptions // Build tool response parts from resume options List toolResponseParts = new java.util.ArrayList<>(); + // Collect tool names/refs from respond directives + java.util.Set respondedTools = new java.util.HashSet<>(); + // Handle respond directives if (resume.getRespond() != null) { for (ToolResponse toolResponse : resume.getRespond()) { + respondedTools.add( + toolResponse.getName() + + "#" + + (toolResponse.getRef() != null ? toolResponse.getRef() : "")); Part responsePart = new Part(); responsePart.setToolResponse(toolResponse); Map metadata = new java.util.HashMap<>(); @@ -979,52 +1071,63 @@ private ModelRequest handleResumeOption(ModelRequest request, GenerateOptions } } - // Handle restart directives - execute the tools + // Note: restart directives are handled inside the generate loop + // for proper middleware lifecycle (wrapTool and wrapGenerate hooks fire correctly) + boolean hasRespond = resume.getRespond() != null && !resume.getRespond().isEmpty(); + boolean hasRestart = resume.getRestart() != null && !resume.getRestart().isEmpty(); + + if (!hasRespond && !hasRestart) { + throw new GenkitException("Resume options must contain either respond or restart directives"); + } + + // Collect tool names/refs from restart directives to avoid duplicating their responses + java.util.Set restartedTools = new java.util.HashSet<>(); if (resume.getRestart() != null) { - ActionContext ctx = new ActionContext(registry); - for (ToolRequest restartRequest : resume.getRestart()) { - Tool tool = findTool(restartRequest.getName(), options.getTools()); - if (tool == null) { - throw new GenkitException("Tool not found for restart: " + restartRequest.getName()); - } + for (ToolRequest toolRequest : resume.getRestart()) { + restartedTools.add( + toolRequest.getName() + + "#" + + (toolRequest.getRef() != null ? toolRequest.getRef() : "")); + } + } - try { - @SuppressWarnings("unchecked") - Tool typedTool = (Tool) tool; - Object result = typedTool.run(ctx, restartRequest.getInput()); - - Part responsePart = new Part(); - ToolResponse toolResponse = - new ToolResponse(restartRequest.getRef(), restartRequest.getName(), result); - responsePart.setToolResponse(toolResponse); - Map metadata = new java.util.HashMap<>(); - metadata.put("source", "restart"); - responsePart.setMetadata(metadata); - toolResponseParts.add(responsePart); - } catch (ToolInterruptException e) { - // Tool interrupted again during restart - throw new GenkitException( - "Tool '" - + restartRequest.getName() - + "' triggered an interrupt during restart. " - + "Re-interrupting during restart is not supported."); + // Add tool responses for completed tools (pendingOutput metadata) that aren't + // being explicitly responded to or restarted. This ensures all tool_calls in the + // model message have matching tool responses (required by providers like OpenAI). + for (Part part : lastMessage.getContent()) { + if (part.getToolRequest() != null && part.getMetadata() != null) { + Object pendingOutput = part.getMetadata().get("pendingOutput"); + if (pendingOutput != null) { + String key = + part.getToolRequest().getName() + + "#" + + (part.getToolRequest().getRef() != null ? part.getToolRequest().getRef() : ""); + if (!respondedTools.contains(key) && !restartedTools.contains(key)) { + Part responsePart = new Part(); + ToolResponse toolResponse = + new ToolResponse( + part.getToolRequest().getRef(), part.getToolRequest().getName(), pendingOutput); + responsePart.setToolResponse(toolResponse); + Map metadata = new java.util.HashMap<>(); + metadata.put("pendingOutput", true); + responsePart.setMetadata(metadata); + toolResponseParts.add(responsePart); + } } } } - if (toolResponseParts.isEmpty()) { - throw new GenkitException("Resume options must contain either respond or restart directives"); + if (!toolResponseParts.isEmpty()) { + // Add tool response message for completed and responded tools + Message toolResponseMessage = new Message(); + toolResponseMessage.setRole(Role.TOOL); + toolResponseMessage.setContent(toolResponseParts); + Map toolMsgMetadata = new java.util.HashMap<>(); + toolMsgMetadata.put("resumed", true); + toolResponseMessage.setMetadata(toolMsgMetadata); + messages.add(toolResponseMessage); } - // Add tool response message - Message toolResponseMessage = new Message(); - toolResponseMessage.setRole(Role.TOOL); - toolResponseMessage.setContent(toolResponseParts); - Map toolMsgMetadata = new java.util.HashMap<>(); - toolMsgMetadata.put("resumed", true); - toolResponseMessage.setMetadata(toolMsgMetadata); - messages.add(toolResponseMessage); - return ModelRequest.builder() .messages(messages) .config(request.getConfig()) diff --git a/samples/middleware-v2/src/main/java/com/google/genkit/samples/MiddlewareInterruptRestartSample.java b/samples/middleware-v2/src/main/java/com/google/genkit/samples/MiddlewareInterruptRestartSample.java new file mode 100644 index 000000000..8400c1034 --- /dev/null +++ b/samples/middleware-v2/src/main/java/com/google/genkit/samples/MiddlewareInterruptRestartSample.java @@ -0,0 +1,385 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.genkit.samples; + +import com.google.genkit.Genkit; +import com.google.genkit.GenkitOptions; +import com.google.genkit.ai.GenerateOptions; +import com.google.genkit.ai.ModelResponse; +import com.google.genkit.ai.Part; +import com.google.genkit.ai.ResumeOptions; +import com.google.genkit.ai.Tool; +import com.google.genkit.ai.ToolInterruptException; +import com.google.genkit.ai.ToolRequest; +import com.google.genkit.ai.middleware.BaseGenerationMiddleware; +import com.google.genkit.ai.middleware.GenerateNext; +import com.google.genkit.ai.middleware.GenerateParams; +import com.google.genkit.ai.middleware.GenerationMiddleware; +import com.google.genkit.ai.middleware.ModelNext; +import com.google.genkit.ai.middleware.ModelParams; +import com.google.genkit.ai.middleware.ToolNext; +import com.google.genkit.ai.middleware.ToolParams; +import com.google.genkit.core.ActionContext; +import com.google.genkit.core.GenkitException; +import com.google.genkit.plugins.openai.OpenAIPlugin; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Sample that tests the middleware lifecycle during interrupt restart. + * + *

This validates Pavel's feedback on PR #125: when restarting an interrupted tool, the + * middleware must follow the correct nested lifecycle. Specifically: + * + *

+ * === Initial generate call (triggers tool4 interrupt mid-flow) ===
+ *
+ * generate - 1
+ *     model
+ *     tool1
+ *     tool2
+ *     tool3
+ *     generate - 2
+ *         model
+ *         tool4  // <--- INTERRUPT
+ *
+ * === Restart of tool4 (correct lifecycle) ===
+ *
+ * generate - 1          // restart generate call
+ *     tool4             // RESTART (through wrapTool middleware)
+ *     generate - 2      // nested - NOT flat!
+ *         model
+ *         // done
+ * 
+ * + *

The WRONG (naive) implementation would flatten this to: + * + *

+ * generate - 1
+ *     tool4   // RESTART
+ *     model   // flat - no nested generate
+ *     // done
+ * 
+ * + *

This sample uses a lifecycle-tracking middleware that records every hook invocation, then + * verifies the correct nesting after restart. + * + *

To run: + * + *

    + *
  1. Set the OPENAI_API_KEY environment variable + *
  2. Run: mvn exec:java + * -Dexec.mainClass="com.google.genkit.samples.MiddlewareInterruptRestartSample" -pl + * samples/middleware-v2 + *
+ */ +public class MiddlewareInterruptRestartSample { + + // ========================================================================= + // Lifecycle-tracking middleware + // ========================================================================= + + /** + * Middleware that records every hook invocation as a structured log entry. Used to verify the + * correct nesting of generate/model/tool calls. + */ + static class LifecycleTracker extends BaseGenerationMiddleware { + + private final List log; + private final AtomicInteger depth = new AtomicInteger(0); + + LifecycleTracker(List log) { + this.log = log; + } + + @Override + public String name() { + return "lifecycle-tracker"; + } + + @Override + public GenerationMiddleware newInstance() { + return new LifecycleTracker(log); + } + + private String indent() { + return " ".repeat(depth.get()); + } + + @Override + public ModelResponse wrapGenerate(ActionContext ctx, GenerateParams params, GenerateNext next) + throws GenkitException { + String entry = indent() + "generate - " + (params.getIteration() + 1); + log.add(entry); + System.out.println(entry); + depth.incrementAndGet(); + try { + return next.apply(ctx, params); + } finally { + depth.decrementAndGet(); + } + } + + @Override + public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next) + throws GenkitException { + String entry = indent() + "model"; + log.add(entry); + System.out.println(entry); + return next.apply(ctx, params); + } + + @Override + public Part wrapTool(ActionContext ctx, ToolParams params, ToolNext next) + throws GenkitException { + String toolName = params.getRequest().getName(); + String entry = indent() + toolName; + log.add(entry); + System.out.println(entry); + return next.apply(ctx, params); + } + } + + // ========================================================================= + // Data classes + // ========================================================================= + + public static class ActionInput { + private String action; + + public ActionInput() {} + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + } + + // ========================================================================= + // Main + // ========================================================================= + + public static void main(String[] args) throws Exception { + Genkit genkit = + Genkit.builder() + .options(GenkitOptions.builder().devMode(false).build()) + .plugin(OpenAIPlugin.create()) + .build(); + + // Define regular tools (tool1, tool2, tool3) with object input schemas (required by OpenAI) + @SuppressWarnings("unchecked") + Tool, String> tool1 = + genkit.defineTool( + "tool1", + "First tool - runs task 1", + Map.of("type", "object", "properties", Map.of(), "additionalProperties", false), + (Class>) (Class) Map.class, + (ctx, input) -> "tool1-result"); + + @SuppressWarnings("unchecked") + Tool, String> tool2 = + genkit.defineTool( + "tool2", + "Second tool - runs task 2", + Map.of("type", "object", "properties", Map.of(), "additionalProperties", false), + (Class>) (Class) Map.class, + (ctx, input) -> "tool2-result"); + + @SuppressWarnings("unchecked") + Tool, String> tool3 = + genkit.defineTool( + "tool3", + "Third tool - runs task 3", + Map.of("type", "object", "properties", Map.of(), "additionalProperties", false), + (Class>) (Class) Map.class, + (ctx, input) -> "tool3-result"); + + // Define tool4 as a restartable tool that interrupts on first call. + // On restart (second call), it succeeds. This simulates a tool that needs + // human confirmation before proceeding. + final java.util.concurrent.atomic.AtomicBoolean tool4HasInterrupted = + new java.util.concurrent.atomic.AtomicBoolean(false); + + @SuppressWarnings("unchecked") + Tool, String> tool4 = + genkit.defineTool( + "tool4", + "Fourth tool - requires confirmation, interrupts on first call", + Map.of( + "type", + "object", + "properties", + Map.of("action", Map.of("type", "string")), + "additionalProperties", + false), + (Class>) (Class) Map.class, + (ctx, input) -> { + if (!tool4HasInterrupted.getAndSet(true)) { + // First call: interrupt to request confirmation + throw new ToolInterruptException( + Map.of("reason", "needs confirmation", "action", String.valueOf(input))); + } + // Restart call: proceed normally + return "tool4-confirmed-result"; + }); + + // Shared log across initial + restart calls + List lifecycleLog = Collections.synchronizedList(new ArrayList<>()); + + GenerationMiddleware tracker = new LifecycleTracker(lifecycleLog); + + System.out.println("==========================================================="); + System.out.println(" Middleware Interrupt Restart Lifecycle Test"); + System.out.println("==========================================================="); + System.out.println(); + + // --------------------------------------------------------------- + // Step 1: Initial generate - model should call tools, tool4 interrupts + // --------------------------------------------------------------- + // Note: The actual model call pattern depends on the LLM response. + // We ask it to call all 4 tools. The first 3 succeed, tool4 interrupts. + System.out.println(">>> Step 1: Initial generate (expecting tool4 to interrupt)"); + System.out.println("-----------------------------------------------------------"); + + ModelResponse response = + genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .system( + "You are a task executor. When asked to run all tasks, you MUST call all 4 " + + "tools in order: tool1, tool2, tool3, tool4. Call them all at once.") + .prompt("Run all tasks now.") + .tools(List.of(tool1, tool2, tool3, tool4)) + .use(tracker) + .maxTurns(5) + .build()); + + System.out.println(); + if (response.isInterrupted()) { + System.out.println(">>> tool4 interrupted as expected!"); + System.out.println(); + + // --------------------------------------------------------------- + // Step 2: Restart tool4 with middleware - should fire wrapTool + nested wrapGenerate + // --------------------------------------------------------------- + System.out.println(">>> Step 2: Restart tool4 (expecting nested lifecycle)"); + System.out.println("-----------------------------------------------------------"); + + // Find the interrupted tool request + Part interruptPart = response.getInterrupts().get(0); + ToolRequest interruptedRequest = interruptPart.getToolRequest(); + + // Create restart request (re-execute tool4 with same input) + ToolRequest restartRequest = new ToolRequest(); + restartRequest.setName(interruptedRequest.getName()); + restartRequest.setRef(interruptedRequest.getRef()); + restartRequest.setInput(interruptedRequest.getInput()); + + ModelResponse resumedResponse = + genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .messages(response.getMessages()) + .tools(List.of(tool1, tool2, tool3, tool4)) + .use(tracker) + .resume(ResumeOptions.builder().restart(restartRequest).build()) + .maxTurns(5) + .build()); + + System.out.println(); + System.out.println(">>> Restart completed. Final response:"); + System.out.println(resumedResponse.getText()); + } else { + System.out.println(">>> No interrupt occurred (model didn't call tool4)."); + System.out.println(">>> Response: " + response.getText()); + } + + // --------------------------------------------------------------- + // Print full lifecycle log + // --------------------------------------------------------------- + System.out.println(); + System.out.println("==========================================================="); + System.out.println(" Full Lifecycle Log"); + System.out.println("==========================================================="); + for (String entry : lifecycleLog) { + System.out.println(entry); + } + + System.out.println(); + System.out.println("==========================================================="); + System.out.println(" Verification"); + System.out.println("==========================================================="); + + // Verify that the restart lifecycle shows nested generate calls + // After restart, we expect to see at minimum: + // generate - 1 (restart iteration) + // tool4 (through wrapTool) + // generate - 2 (nested, NOT flat) + // model (model call after tool4 completes) + boolean foundRestartGenerate = false; + boolean foundRestartTool = false; + boolean foundNestedGenerate = false; + boolean foundNestedModel = false; + + // Look at the restart portion of the log (after the initial call) + boolean inRestartPhase = false; + for (String entry : lifecycleLog) { + // The restart phase starts with the second "generate - 1" + if (!inRestartPhase && entry.trim().equals("generate - 1")) { + if (foundRestartGenerate) { + // This is the second "generate - 1", so we're in restart phase + inRestartPhase = true; + foundRestartGenerate = true; + continue; + } + foundRestartGenerate = true; + } + if (inRestartPhase) { + if (entry.trim().equals("tool4")) { + foundRestartTool = true; + } + if (entry.trim().equals("generate - 2")) { + foundNestedGenerate = true; + } + if (foundNestedGenerate && entry.trim().equals("model")) { + foundNestedModel = true; + } + } + } + + System.out.println("Restart fires wrapTool for tool4: " + foundRestartTool); + System.out.println("Restart has nested generate: " + foundNestedGenerate); + System.out.println("Nested generate calls model: " + foundNestedModel); + + if (foundRestartTool && foundNestedGenerate && foundNestedModel) { + System.out.println(); + System.out.println("PASS: Restart follows correct nested lifecycle!"); + } else { + System.out.println(); + System.out.println("FAIL: Restart lifecycle is flat (naive implementation)."); + System.out.println(" Expected: generate -> tool4 -> generate -> model"); + } + } +} From 52a944112449a12e15cb759bda7b823ba4747b0c Mon Sep 17 00:00:00 2001 From: xavidop Date: Mon, 13 Apr 2026 22:11:36 +0200 Subject: [PATCH 50/50] feat: udpated docs --- docs/src/content/docs/middleware.md | 250 +++++++++++++++++++++++++--- 1 file changed, 229 insertions(+), 21 deletions(-) diff --git a/docs/src/content/docs/middleware.md b/docs/src/content/docs/middleware.md index 216848067..89c945724 100644 --- a/docs/src/content/docs/middleware.md +++ b/docs/src/content/docs/middleware.md @@ -1,11 +1,18 @@ --- title: Middleware -description: Add cross-cutting concerns to your AI workflows with middleware. +description: Add cross-cutting concerns to your AI workflows with flow middleware and generation middleware. --- -Middleware lets you intercept and modify the behavior of flow executions. Use middleware for logging, caching, rate limiting, retries, input validation, and more. Middleware follows the chain-of-responsibility pattern — each middleware can modify the request, call the next handler, and modify the response. +Middleware lets you intercept and modify the behavior of flow executions and AI generation. Genkit provides two middleware systems: -## Defining middleware +- **Flow Middleware** — wraps the entire flow function. Use for logging, caching, rate limiting, retries, and input validation. +- **Generation Middleware (V2)** — hooks into the `generate()` pipeline at three levels: model calls, tool executions, and loop iterations. Use for metering, observability, and tool interception. + +## Flow Middleware + +Flow middleware follows the chain-of-responsibility pattern — each middleware can modify the request, call the next handler, and modify the response. + +### Defining middleware A middleware is a function that receives the request, an `ActionContext`, and a `next` function to call the next handler in the chain: @@ -20,7 +27,7 @@ Middleware loggingMiddleware = (request, context, next) -> { }; ``` -## Attaching middleware to flows +### Attaching middleware to flows Pass middleware as a list when defining a flow: @@ -46,11 +53,11 @@ Flow chatFlow = genkit.defineFlow( Middleware executes in order — the first middleware in the list runs first (outermost), wrapping all subsequent middleware and the flow handler. -## Built-in middleware +### Built-in middleware The `CommonMiddleware` class provides factory methods for common patterns: -### Logging +#### Logging ```java import com.google.genkit.core.middleware.CommonMiddleware; @@ -62,7 +69,7 @@ Middleware logging = CommonMiddleware.logging("chat"); Middleware logging = CommonMiddleware.logging("chat", myLogger); ``` -### Retry with exponential backoff +#### Retry with exponential backoff ```java // Retry up to 3 times with 100ms initial delay @@ -73,7 +80,7 @@ Middleware retry = CommonMiddleware.retry(3, 100, error -> error.getMessage().contains("rate limit")); ``` -### Input validation +#### Input validation ```java Middleware validate = CommonMiddleware.validate(input -> { @@ -86,7 +93,7 @@ Middleware validate = CommonMiddleware.validate(input -> { }); ``` -### Request and response transformation +#### Request and response transformation ```java // Sanitize input @@ -98,7 +105,7 @@ Middleware format = CommonMiddleware.transformResponse( output -> "[" + Instant.now() + "] " + output); ``` -### Caching +#### Caching ```java import com.google.genkit.core.middleware.MiddlewareCache; @@ -111,28 +118,28 @@ Middleware cache = CommonMiddleware.cache( The `MiddlewareCache` interface requires `get(String key)` and `put(String key, O value)` methods. -### Rate limiting +#### Rate limiting ```java // Max 10 requests per 60 seconds Middleware rateLimit = CommonMiddleware.rateLimit(10, 60_000); ``` -### Timeout +#### Timeout ```java // 30 second timeout Middleware timeout = CommonMiddleware.timeout(30_000); ``` -### Error handling +#### Error handling ```java Middleware errorHandler = CommonMiddleware.errorHandler( error -> "Sorry, something went wrong: " + error.getMessage()); ``` -### Conditional middleware +#### Conditional middleware Apply middleware only when a condition is met: @@ -143,7 +150,7 @@ Middleware conditional = CommonMiddleware.conditional( ); ``` -### Before/after hooks +#### Before/after hooks ```java Middleware hooks = CommonMiddleware.beforeAfter( @@ -152,14 +159,14 @@ Middleware hooks = CommonMiddleware.beforeAfter( ); ``` -### Timing +#### Timing ```java Middleware timing = CommonMiddleware.timing( duration -> System.out.println("Took " + duration + "ms")); ``` -## Building a middleware chain +### Building a middleware chain Use `MiddlewareChain` for more control over middleware ordering: @@ -182,7 +189,7 @@ String result = chain.execute(input, context, (ctx, req) -> { }); ``` -## Custom middleware example +### Custom middleware example A metrics-collecting middleware: @@ -207,7 +214,7 @@ Middleware metricsMiddleware = (request, context, next) -> { }; ``` -## Built-in middleware reference +### Built-in middleware reference | Factory Method | Description | |---------------|-------------| @@ -224,7 +231,208 @@ Middleware metricsMiddleware = (request, context, next) -> { | `beforeAfter(before, after)` | Run hooks before and after | | `timing(callback)` | Measure execution duration | +## Generation Middleware (V2) + +Generation Middleware provides fine-grained hooks into the generation pipeline, letting you intercept model calls, tool executions, and generate loop iterations independently. Unlike flow-level middleware (which wraps the entire flow function), Generation Middleware operates inside `generate()` and is attached per call. + +### Three hooks + +| Hook | Wraps | Receives | Use cases | +|------|-------|----------|-----------| +| `wrapGenerate` | Each iteration of the tool loop | `GenerateParams` (request + iteration number) | Timing, logging per turn, retry logic | +| `wrapModel` | Each model API call | `ModelParams` (request + stream callback) | Token metering, request/response rewriting, caching | +| `wrapTool` | Each tool execution | `ToolParams` (request part + resolved tool) | Tool authorization, audit logging, error handling | + +Hooks nest naturally: `wrapGenerate` is the outermost layer, `wrapModel` runs inside it, and `wrapTool` runs for each tool the model requests. + +``` +wrapGenerate (iteration 0) +├── wrapModel → model API call +├── wrapTool → tool1 +├── wrapTool → tool2 +└── wrapGenerate (iteration 1) ← recursive via tool loop + ├── wrapModel → model API call + └── (no more tool calls → return) +``` + +### Defining Generation Middleware + +Implement the `GenerationMiddleware` interface or extend `BaseGenerationMiddleware` (which passes through by default). Override only the hooks you need: + +```java +import com.google.genkit.ai.middleware.BaseGenerationMiddleware; +import com.google.genkit.ai.middleware.GenerationMiddleware; +import com.google.genkit.ai.middleware.ModelNext; +import com.google.genkit.ai.middleware.ModelParams; + +class TokenMeteringMiddleware extends BaseGenerationMiddleware { + + private final AtomicInteger totalTokens = new AtomicInteger(0); + + @Override + public String name() { + return "token-metering"; + } + + @Override + public GenerationMiddleware newInstance() { + return new TokenMeteringMiddleware(); // fresh counters per generate() + } + + @Override + public ModelResponse wrapModel(ActionContext ctx, ModelParams params, ModelNext next) + throws GenkitException { + ModelResponse response = next.apply(ctx, params); + // Inspect response for token usage + logger.info("Tokens used: {}", response.getUsage()); + return response; + } +} +``` + +Key points: + +- **`name()`** — unique identifier for the middleware. +- **`newInstance()`** — called once per `generate()` invocation. Return a fresh object so per-request state (counters, timers) is isolated. Stateless middleware can return `this`. +- **`next.apply(ctx, params)`** — calls the next middleware in the chain (or the core handler). You must call it to continue the pipeline. Skip it to short-circuit (e.g., return a cached response). + +### Attaching middleware to generate() + +Use `GenerateOptions.builder().use()`: + +```java +GenerationMiddleware metering = new TokenMeteringMiddleware(); +GenerationMiddleware logging = new ModelLoggingMiddleware(); + +ModelResponse response = genkit.generate( + GenerateOptions.builder() + .model("openai/gpt-4o-mini") + .prompt("Explain middleware") + .use(metering, logging) + .build()); +``` + +Middleware order matters — the **first** middleware listed is **outermost** (runs first on the way in, last on the way out). + +### Multi-hook middleware + +A single middleware can implement all three hooks to observe every stage: + +```java +class FullObservabilityMiddleware extends BaseGenerationMiddleware { + + private final AtomicInteger iterations = new AtomicInteger(0); + private final AtomicInteger modelCalls = new AtomicInteger(0); + private final AtomicInteger toolCalls = new AtomicInteger(0); + + @Override + public String name() { return "full-observability"; } + + @Override + public GenerationMiddleware newInstance() { + return new FullObservabilityMiddleware(); + } + + @Override + public ModelResponse wrapGenerate(ActionContext ctx, GenerateParams params, + GenerateNext next) throws GenkitException { + int iter = iterations.incrementAndGet(); + logger.info("=== Generate iteration {} ===", iter); + ModelResponse resp = next.apply(ctx, params); + logger.info("=== Iteration {} done (model: {}, tools: {}) ===", + iter, modelCalls.get(), toolCalls.get()); + return resp; + } + + @Override + public ModelResponse wrapModel(ActionContext ctx, ModelParams params, + ModelNext next) throws GenkitException { + modelCalls.incrementAndGet(); + return next.apply(ctx, params); + } + + @Override + public Part wrapTool(ActionContext ctx, ToolParams params, + ToolNext next) throws GenkitException { + toolCalls.incrementAndGet(); + logger.info("Tool: {}", params.getRequest().getName()); + return next.apply(ctx, params); + } +} +``` + +### Middleware-provided tools + +Middleware can inject additional tools into the generation by overriding `tools()`: + +```java +@Override +public List> tools() { + return List.of(myCustomTool); +} +``` + +These tools are merged with the tools from `GenerateOptions.tools()` and are available for the model to call. + +### Middleware with interrupts and restarts + +Generation Middleware integrates with the [interrupt system](/docs/interrupts). When a tool throws `ToolInterruptException`, the `wrapTool` hook still fires — the exception propagates through the middleware chain, so you can observe or handle it. + +When resuming with `ResumeOptions.builder().restart(toolRequest)`, the restarted tool executes through the full `wrapTool` chain, and the subsequent model call goes through a new `wrapGenerate` iteration. This ensures middleware sees every operation regardless of whether it was an initial call or a restart. + +``` +Initial generate: + wrapGenerate(0) + ├── wrapModel → model requests tool4 + ├── wrapTool → tool1 (completes) + ├── wrapTool → tool2 (completes) + └── wrapTool → tool4 (interrupts!) → return interrupted response + +Restart generate: + wrapTool → tool4 (restart, completes) + wrapGenerate(1) + ├── wrapModel → model returns final answer + └── return response +``` + +### BaseGenerationMiddleware + +`BaseGenerationMiddleware` provides pass-through defaults for all hooks. Extend it to override only what you need: + +```java +class TimingMiddleware extends BaseGenerationMiddleware { + + @Override + public String name() { return "timing"; } + + @Override + public GenerationMiddleware newInstance() { return new TimingMiddleware(); } + + @Override + public ModelResponse wrapGenerate(ActionContext ctx, GenerateParams params, + GenerateNext next) throws GenkitException { + long start = System.currentTimeMillis(); + ModelResponse resp = next.apply(ctx, params); + logger.info("Iteration {} took {}ms", + params.getIteration(), System.currentTimeMillis() - start); + return resp; + } +} +``` + +### Generation Middleware vs Flow Middleware + +| | Flow Middleware | Generation Middleware (V2) | +|---|---|---| +| **Scope** | The entire flow function | Inside `generate()` — model, tools, iterations | +| **Attached to** | `defineFlow(..., middleware)` | `GenerateOptions.builder().use()` | +| **Typed to** | Flow input/output types | `ModelRequest` / `ModelResponse` / `Part` | +| **State** | Shared across requests | Fresh per `generate()` via `newInstance()` | +| **Best for** | Auth, rate limiting, validation | Observability, metering, tool interception | + +You can use both together — flow middleware wraps the outer flow, and generation middleware wraps the inner AI pipeline. + ## Samples -- [middleware sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/middleware) — Custom and built-in middleware patterns -- [middleware-v2 sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/middleware-v2) — Updated middleware approaches +- [middleware sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/middleware) — Flow-level middleware patterns (logging, retry, caching, validation) +- [middleware-v2 sample](https://github.com/genkit-ai/genkit-java/tree/main/samples/middleware-v2) — Generation Middleware with all three hooks and interrupt/restart lifecycle