From cb8f0bd6af2d9a64a955c628256973eacb5d620e Mon Sep 17 00:00:00 2001 From: Alex Astrum Date: Tue, 3 Dec 2024 11:48:43 -0500 Subject: [PATCH 001/562] Fix infinite loop for multi-page MCP resources (#1440) --- js/plugins/mcp/src/client/resources.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/js/plugins/mcp/src/client/resources.ts b/js/plugins/mcp/src/client/resources.ts index 779ec9d52..00c45c381 100644 --- a/js/plugins/mcp/src/client/resources.ts +++ b/js/plugins/mcp/src/client/resources.ts @@ -43,11 +43,11 @@ export async function registerResourceTools( return client.listResources(); } - let currentCursor: string | undefined; + let currentCursor: string | undefined = cursor; const resources: Resource[] = []; while (true) { const { nextCursor, resources: newResources } = - await client.listResources({ cursor }); + await client.listResources({ cursor: currentCursor }); resources.push(...newResources); currentCursor = nextCursor; if (!currentCursor) break; @@ -72,11 +72,11 @@ export async function registerResourceTools( return client.listResourceTemplates(); } - let currentCursor: string | undefined; + let currentCursor: string | undefined = cursor; const resourceTemplates: ResourceTemplate[] = []; while (true) { const { nextCursor, resourceTemplates: newResourceTemplates } = - await client.listResourceTemplates({ cursor }); + await client.listResourceTemplates({ cursor: currentCursor }); resourceTemplates.push(...newResourceTemplates); currentCursor = nextCursor; if (!currentCursor) break; From fdbba79d6d698cc558b33ff7cf514d62dac2f54f Mon Sep 17 00:00:00 2001 From: alonsopec89 Date: Tue, 3 Dec 2024 12:11:51 -0600 Subject: [PATCH 002/562] 1174 set log level to debug in golang (#1236) * put the level to debug --- go/core/logger/logger.go | 28 ++++++++++++++++------ go/genkit/servers.go | 6 ++--- go/internal/registry/registry.go | 2 +- go/plugins/googlecloud/googlecloud_test.go | 2 +- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/go/core/logger/logger.go b/go/core/logger/logger.go index 9ab8d36c2..11f37014e 100644 --- a/go/core/logger/logger.go +++ b/go/core/logger/logger.go @@ -19,20 +19,34 @@ import ( "context" "log/slog" "os" + "sync" "github.com/firebase/genkit/go/internal/base" ) -func init() { - // TODO: Remove this. The main program should be responsible for configuring logging. - // This is just a convenience during development. +var ( + logLevel = slog.LevelDebug + mu sync.RWMutex + loggerKey = base.NewContextKey[*slog.Logger]() +) + +// SetLevel sets the global log level +func SetLevel(level slog.Level) { + mu.Lock() + defer mu.Unlock() + logLevel = level h := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ - Level: slog.LevelDebug, - })) - slog.SetDefault(h) + Level: logLevel, + })) + slog.SetDefault(h) } -var loggerKey = base.NewContextKey[*slog.Logger]() +// GetLevel gets the current global log level +func GetLevel() slog.Level { + mu.RLock() + defer mu.RUnlock() + return logLevel +} // FromContext returns the Logger in ctx, or the default Logger // if there is none. diff --git a/go/genkit/servers.go b/go/genkit/servers.go index 4453c25ab..e69dab2b8 100644 --- a/go/genkit/servers.go +++ b/go/genkit/servers.go @@ -157,7 +157,7 @@ func findProjectRoot() (string, error) { // // To construct a server with additional routes, use [NewFlowServeMux]. func startFlowServer(addr string, flows []string, errCh chan<- error) *http.Server { - slog.Info("starting flow server") + slog.Debug("starting flow server") addr = serverAddress(addr, "PORT", "127.0.0.1:3400") mux := NewFlowServeMux(flows) return startServer(addr, mux, errCh) @@ -181,7 +181,7 @@ func startServer(addr string, handler http.Handler, errCh chan<- error) *http.Se } go func() { - slog.Info("server listening", "addr", addr) + slog.Debug("server listening", "addr", addr) if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { errCh <- fmt.Errorf("server error on %s: %w", addr, err) } @@ -204,7 +204,7 @@ func shutdownServers(servers []*http.Server) error { if err := srv.Shutdown(ctx); err != nil { slog.Error("server shutdown failed", "addr", srv.Addr, "err", err) } else { - slog.Info("server shutdown successfully", "addr", srv.Addr) + slog.Debug("server shutdown successfully", "addr", srv.Addr) } }(server) } diff --git a/go/internal/registry/registry.go b/go/internal/registry/registry.go index 4edfed350..da8348ab8 100644 --- a/go/internal/registry/registry.go +++ b/go/internal/registry/registry.go @@ -77,7 +77,7 @@ func (r *Registry) RegisterAction(typ atype.ActionType, a action.Action) { } a.SetTracingState(r.tstate) r.actions[key] = a - slog.Info("RegisterAction", + slog.Debug("RegisterAction", "type", typ, "name", a.Name()) } diff --git a/go/plugins/googlecloud/googlecloud_test.go b/go/plugins/googlecloud/googlecloud_test.go index 111aa7c7d..22a46b28b 100644 --- a/go/plugins/googlecloud/googlecloud_test.go +++ b/go/plugins/googlecloud/googlecloud_test.go @@ -74,7 +74,7 @@ func TestGCP(t *testing.T) { if err := setLogHandler(*projectID, slog.LevelInfo); err != nil { t.Fatal(err) } - slog.Info("testing GCP logging", + slog.Debug("testing GCP logging", "binaryName", os.Args[0], "goVersion", runtime.Version()) // Allow time to export. From f91b09830b39c92c3152761bc2072c35c8de4590 Mon Sep 17 00:00:00 2001 From: Max Lord Date: Tue, 3 Dec 2024 15:37:37 -0500 Subject: [PATCH 003/562] Setting root span status explicitly (#1449) --- js/plugins/google-cloud/src/gcpOpenTelemetry.ts | 2 ++ js/plugins/google-cloud/tests/traces_test.ts | 3 +++ 2 files changed, 5 insertions(+) diff --git a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts index 38029ef3d..c004e1b8d 100644 --- a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts +++ b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts @@ -337,6 +337,8 @@ class AdjustingTraceExporter implements SpanExporter { featuresTelemetry.tick(span, unused, this.logIO, this.projectId); // Report executions and latency for all flow paths only on the root span pathsTelemetry.tick(span, paths, this.logIO, this.projectId); + // Set root status explicitly + span.attributes['genkit:rootState'] = span.attributes['genkit:state']; } if (type === 'action' && subtype === 'model') { // Report generate metrics () for all model actions diff --git a/js/plugins/google-cloud/tests/traces_test.ts b/js/plugins/google-cloud/tests/traces_test.ts index a1690bf80..ffa401c16 100644 --- a/js/plugins/google-cloud/tests/traces_test.ts +++ b/js/plugins/google-cloud/tests/traces_test.ts @@ -152,6 +152,8 @@ describe('GoogleCloudTracing', () => { spans[0].attributes['genkit/failedPath'], '/{badFlow,t:flow}/{badStep,t:flowStep}' ); + assert.equal(spans[1].attributes['genkit/isRoot'], true); + assert.equal(spans[1].attributes['genkit/rootState'], 'error'); }); it('labels the root feature', async () => { @@ -165,6 +167,7 @@ describe('GoogleCloudTracing', () => { assert.equal(spans[0].attributes['genkit/feature'], undefined); assert.equal(spans[1].name, 'niceFlow'); assert.equal(spans[1].attributes['genkit/feature'], 'niceFlow'); + assert.equal(spans[1].attributes['genkit/rootState'], 'success'); }); it('adds the genkit/model label for model actions', async () => { From 0c3303ca8ad9d46d2c9e2ccdeefb74b3aee496b4 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Tue, 3 Dec 2024 15:20:50 -0800 Subject: [PATCH 004/562] fix(js/ai): Ensure messages are serializable form before being sent to SessionStore. (#1445) --- js/ai/src/session.ts | 9 ++++----- js/genkit/tests/chat_test.ts | 2 +- js/genkit/tests/session_test.ts | 28 ++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/js/ai/src/session.ts b/js/ai/src/session.ts index db68ce76b..6e84c3039 100644 --- a/js/ai/src/session.ts +++ b/js/ai/src/session.ts @@ -102,10 +102,7 @@ export class Session { /** * Update messages for a given thread. */ - async updateMessages( - thread: string, - messasges: MessageData[] - ): Promise { + async updateMessages(thread: string, messages: MessageData[]): Promise { let sessionData = this.sessionData; if (!sessionData) { sessionData = {} as SessionData; @@ -113,7 +110,9 @@ export class Session { if (!sessionData.threads) { sessionData.threads = {}; } - sessionData.threads[thread] = messasges; + sessionData.threads[thread] = messages.map((m: any) => + m.toJSON ? m.toJSON() : m + ); this.sessionData = sessionData; await this.store.save(this.id, sessionData); diff --git a/js/genkit/tests/chat_test.ts b/js/genkit/tests/chat_test.ts index 6c10f92bc..1d6c148de 100644 --- a/js/genkit/tests/chat_test.ts +++ b/js/genkit/tests/chat_test.ts @@ -466,7 +466,7 @@ describe('preamble', () => { }); }); - it('updates the preabmle on fresh chat instance', async () => { + it('updates the preamble on fresh chat instance', async () => { const agent = ai.definePrompt( { name: 'agent', diff --git a/js/genkit/tests/session_test.ts b/js/genkit/tests/session_test.ts index 5014e8544..d49e01fc5 100644 --- a/js/genkit/tests/session_test.ts +++ b/js/genkit/tests/session_test.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { Message } from '@genkit-ai/ai'; +import { SessionStore } from '@genkit-ai/ai/session'; import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; import { Genkit, genkit } from '../src/genkit'; @@ -59,6 +61,32 @@ describe('session', () => { ]); }); + it('sends ready-to-serialize data to the session store', async () => { + let savedData: any; + const store: SessionStore = { + save: async (id, data) => { + savedData = data; + }, + get: async (id) => savedData, + }; + const session = ai.createSession({ + store, + initialState: { foo: 'bar' }, + }); + + session.updateMessages('main', [ + new Message({ role: 'user', content: [{ text: 'hello there' }] }), + ]); + + assert.deepStrictEqual(savedData, { + id: session.id, + state: { foo: 'bar' }, + threads: { + main: [{ content: [{ text: 'hello there' }], role: 'user' }], + }, + }); + }); + it('maintains multithreaded history in the session', async () => { const store = new TestMemorySessionStore(); const session = ai.createSession({ From 0dbc518a32bcaa7fa1ca69da541f90efd8cfc4b2 Mon Sep 17 00:00:00 2001 From: Jacob Cable <32874567+cabljac@users.noreply.github.com> Date: Wed, 4 Dec 2024 14:37:59 +0000 Subject: [PATCH 005/562] feat(js/plugins/vertexai): add Mistral MaaS integration (#1436) * feat(js/plugins/vertexai): add initial Mistral MaaS integration * feat(js/plugins/vertexai): add Mistral MaaS tool calling * refactor(js/plugins/vertexai): use simpler approach for mistral client * feat(js/plugins/vertexai): add streaming mode for Mistral Maas * docs(js/plugins/vertexai): add mistral MaaS docs * refactor(js/testapps): combine modelgarden test apps * chore(js/plugins/vertexai): bump mistral sdk --- docs/plugins/vertex-ai.md | 64 ++ js/plugins/vertexai/package.json | 1 + js/plugins/vertexai/src/modelgarden/index.ts | 9 + .../vertexai/src/modelgarden/mistral.ts | 487 +++++++++++++ .../tests/{ => modelgarden}/anthropic_test.ts | 2 +- .../tests/modelgarden/mistral_test.ts | 688 ++++++++++++++++++ js/pnpm-lock.yaml | 161 +++- .../package.json | 6 +- .../src/anthropic.ts} | 0 .../vertexai-modelgarden/src/mistral.ts | 107 +++ .../tsconfig.json | 0 11 files changed, 1489 insertions(+), 36 deletions(-) create mode 100644 js/plugins/vertexai/src/modelgarden/mistral.ts rename js/plugins/vertexai/tests/{ => modelgarden}/anthropic_test.ts (99%) create mode 100644 js/plugins/vertexai/tests/modelgarden/mistral_test.ts rename js/testapps/{anthropic-models => vertexai-modelgarden}/package.json (79%) rename js/testapps/{anthropic-models/src/index.ts => vertexai-modelgarden/src/anthropic.ts} (100%) create mode 100644 js/testapps/vertexai-modelgarden/src/mistral.ts rename js/testapps/{anthropic-models => vertexai-modelgarden}/tsconfig.json (100%) diff --git a/docs/plugins/vertex-ai.md b/docs/plugins/vertex-ai.md index 836aa6a91..c954a9ad0 100644 --- a/docs/plugins/vertex-ai.md +++ b/docs/plugins/vertex-ai.md @@ -257,6 +257,70 @@ const llmResponse = await ai.generate({ }); ``` +#### Mistral Models on Vertex AI Model Garden + +If you have access to Mistral models ([Mistral Large](https://console.cloud.google.com/vertex-ai/publishers/mistralai/model-garden/mistral-large), [Mistral Nemo](https://console.cloud.google.com/vertex-ai/publishers/mistralai/model-garden/mistral-nemo), or [Codestral](https://console.cloud.google.com/vertex-ai/publishers/mistralai/model-garden/codestral)) in Vertex AI Model Garden, you can use them with Genkit. + +Here's sample configuration for enabling Vertex AI Model Garden models: + +```ts +import { genkit } from 'genkit'; +import { + mistralLarge, + mistralNemo, + codestral, + vertexAIModelGarden, +} from '@genkit-ai/vertexai/modelgarden'; + +const ai = genkit({ + plugins: [ + vertexAIModelGarden({ + location: 'us-central1', + models: [mistralLarge, mistralNemo, codestral], + }), + ], +}); +``` + +Then use them as regular models: + +```ts +const llmResponse = await ai.generate({ + model: mistralLarge, + prompt: 'Write a function that adds two numbers together', + config: { + version: 'mistral-large-2411', // Optional: specify model version + temperature: 0.7, // Optional: control randomness (0-1) + maxOutputTokens: 1024, // Optional: limit response length + topP: 0.9, // Optional: nucleus sampling parameter + stopSequences: ['###'], // Optional: stop generation at sequences + } +}); +``` + +The models support: +- `mistralLarge`: Latest Mistral large model with function calling capabilities +- `mistralNemo`: Optimized for efficiency and speed +- `codestral`: Specialized for code generation tasks + +Each model supports streaming responses and function calling: + +```ts +const response = await ai.generateStream({ + model: mistralLarge, + prompt: 'What should I cook tonight?', + tools: ['recipe-finder'], + config: { + version: 'mistral-large-2411', + temperature: 1, + }, +}); + +for await (const chunk of response.stream) { + console.log(chunk.text); +} +``` + ### Evaluators To use the evaluators from Vertex AI Rapid Evaluation, add an `evaluation` block to your `vertexAI` plugin configuration. diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 86ba76f85..251a66d73 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -39,6 +39,7 @@ "@anthropic-ai/vertex-sdk": "^0.4.0", "@google-cloud/aiplatform": "^3.23.0", "@google-cloud/vertexai": "^1.9.0", + "@mistralai/mistralai-gcp": "^1.3.5", "google-auth-library": "^9.14.2", "googleapis": "^140.0.1", "node-fetch": "^3.3.2", diff --git a/js/plugins/vertexai/src/modelgarden/index.ts b/js/plugins/vertexai/src/modelgarden/index.ts index 52ac287bf..288e9e3a1 100644 --- a/js/plugins/vertexai/src/modelgarden/index.ts +++ b/js/plugins/vertexai/src/modelgarden/index.ts @@ -18,6 +18,7 @@ import { Genkit } from 'genkit'; import { GenkitPlugin, genkitPlugin } from 'genkit/plugin'; import { getDerivedParams } from '../common/index.js'; import { SUPPORTED_ANTHROPIC_MODELS, anthropicModel } from './anthropic.js'; +import { SUPPORTED_MISTRAL_MODELS, mistralModel } from './mistral.js'; import { SUPPORTED_OPENAI_FORMAT_MODELS, modelGardenOpenaiCompatibleModel, @@ -39,6 +40,13 @@ export function vertexAIModelGarden(options: PluginOptions): GenkitPlugin { anthropicModel(ai, anthropicEntry[0], projectId, location); return; } + const mistralEntry = Object.entries(SUPPORTED_MISTRAL_MODELS).find( + ([_, value]) => value.name === m.name + ); + if (mistralEntry) { + mistralModel(ai, mistralEntry[0], projectId, location); + return; + } const openaiModel = Object.entries(SUPPORTED_OPENAI_FORMAT_MODELS).find( ([_, value]) => value.name === m.name ); @@ -65,5 +73,6 @@ export { claude3Opus, claude3Sonnet, } from './anthropic.js'; +export { codestral, mistralLarge, mistralNemo } from './mistral.js'; export { llama3, llama31, llama32 } from './model_garden.js'; export type { PluginOptions }; diff --git a/js/plugins/vertexai/src/modelgarden/mistral.ts b/js/plugins/vertexai/src/modelgarden/mistral.ts new file mode 100644 index 000000000..b2e1596de --- /dev/null +++ b/js/plugins/vertexai/src/modelgarden/mistral.ts @@ -0,0 +1,487 @@ +/** + * Copyright 2024 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 { MistralGoogleCloud } from '@mistralai/mistralai-gcp'; +import { + AssistantMessage, + ChatCompletionChoiceFinishReason, + ChatCompletionRequest, + ChatCompletionResponse, + CompletionChunk, + FunctionCall, + Tool as MistralTool, + SystemMessage, + ToolCall, + ToolMessage, + ToolTypes, + UserMessage, +} from '@mistralai/mistralai-gcp/models/components'; +import { + GENKIT_CLIENT_HEADER, + GenerateRequest, + GenerationCommonConfigSchema, + Genkit, + MessageData, + ModelReference, + ModelResponseData, + Part, + Role, + ToolRequestPart, + z, +} from 'genkit'; +import { modelRef } from 'genkit/model'; + +export const MistralConfigSchema = GenerationCommonConfigSchema.extend({ + location: z.string().optional(), + maxOutputTokens: z.number().optional(), + temperature: z.number().optional(), + // TODO: is this supported? + // topK: z.number().optional(), + topP: z.number().optional(), + stopSequences: z.array(z.string()).optional(), +}); + +export const mistralLarge = modelRef({ + name: 'vertexai/mistral-large', + info: { + label: 'Vertex AI Model Garden - Mistral Large', + versions: ['mistral-large-2411', 'mistral-large-2407'], + supports: { + multiturn: true, + media: false, + tools: true, + systemRole: true, + output: ['text'], + }, + }, + configSchema: MistralConfigSchema, +}); + +export const mistralNemo = modelRef({ + name: 'vertexai/mistral-nemo', + info: { + label: 'Vertex AI Model Garden - Mistral Nemo', + versions: ['mistral-nemo-2407'], + supports: { + multiturn: true, + media: false, + tools: false, + systemRole: true, + output: ['text'], + }, + }, + configSchema: MistralConfigSchema, +}); + +export const codestral = modelRef({ + name: 'vertexai/codestral', + info: { + label: 'Vertex AI Model Garden - Codestral', + versions: ['codestral-2405'], + supports: { + multiturn: true, + media: false, + tools: false, + systemRole: true, + output: ['text'], + }, + }, + configSchema: MistralConfigSchema, +}); + +export const SUPPORTED_MISTRAL_MODELS: Record< + string, + ModelReference +> = { + 'mistral-large': mistralLarge, + 'mistral-nemo': mistralNemo, + codestral: codestral, +}; + +// TODO: Do they export a type for this? +type MistralRole = 'assistant' | 'user' | 'tool' | 'system'; + +function toMistralRole(role: Role): MistralRole { + switch (role) { + case 'model': + return 'assistant'; + case 'user': + return 'user'; + case 'tool': + return 'tool'; + case 'system': + return 'system'; + } +} +function toMistralToolRequest(toolRequest: Record): FunctionCall { + if (!toolRequest.name) { + throw new Error('Tool name is required'); + } + + return { + name: toolRequest.name, + // Mistral expects arguments as either a string or object + arguments: + typeof toolRequest.input === 'string' + ? toolRequest.input + : JSON.stringify(toolRequest.input), + }; +} + +export function toMistralRequest( + model: string, + input: GenerateRequest +): ChatCompletionRequest { + const messages = input.messages.map((msg) => { + // Handle regular text messages + if (msg.content.every((part) => part.text)) { + const content = msg.content.map((part) => part.text || '').join(''); + return { + role: toMistralRole(msg.role), + content, + }; + } + + // Handle assistant's tool/function calls + const toolRequest = msg.content.find((part) => part.toolRequest); + if (toolRequest?.toolRequest) { + const functionCall = toMistralToolRequest(toolRequest.toolRequest); + return { + role: 'assistant' as const, + content: null, + toolCalls: [ + { + id: toolRequest.toolRequest.ref, + type: ToolTypes.Function, + function: { + name: functionCall.name, + arguments: functionCall.arguments, + }, + }, + ], + }; + } + + // Handle tool responses + const toolResponse = msg.content.find((part) => part.toolResponse); + if (toolResponse?.toolResponse) { + return { + role: 'tool' as const, + name: toolResponse.toolResponse.name, + content: JSON.stringify(toolResponse.toolResponse.output), + toolCallId: toolResponse.toolResponse.ref, // This must match the id from tool_calls + }; + } + + return { + role: toMistralRole(msg.role), + content: msg.content.map((part) => part.text || '').join(''), + }; + }); + + validateToolSequence(messages); // This line exists but might not be running? + + const request: ChatCompletionRequest = { + model, + messages, + maxTokens: input.config?.maxOutputTokens ?? 1024, + temperature: input.config?.temperature ?? 0.7, + ...(input.config?.topP && { topP: input.config.topP }), + ...(input.config?.stopSequences && { stop: input.config.stopSequences }), + ...(input.tools && { + tools: input.tools.map((tool) => ({ + type: 'function', + function: { + name: tool.name, + description: tool.description, + parameters: tool.inputSchema || {}, + }, + })) as MistralTool[], + }), + }; + + return request; +} +// Helper to convert Mistral AssistantMessage content into Genkit parts +function fromMistralTextPart(content: string): Part { + return { + text: content, + }; +} + +// Helper to convert Mistral ToolCall into Genkit parts +function fromMistralToolCall(toolCall: ToolCall): ToolRequestPart { + if (!toolCall.function) { + throw new Error('Tool call must include a function definition'); + } + + return { + toolRequest: { + ref: toolCall.id, + name: toolCall.function.name, + input: + typeof toolCall.function.arguments === 'string' + ? JSON.parse(toolCall.function.arguments) + : toolCall.function.arguments, + }, + }; +} + +// Converts Mistral AssistantMessage content into Genkit parts +function fromMistralMessage(message: AssistantMessage): Part[] { + const parts: Part[] = []; + + // Handle textual content + if (typeof message.content === 'string') { + parts.push(fromMistralTextPart(message.content)); + } else if (Array.isArray(message.content)) { + // If content is an array of ContentChunk, handle each chunk + message.content.forEach((chunk) => { + if (chunk.type === 'text') { + parts.push(fromMistralTextPart(chunk.text)); + } + // Add support for other ContentChunk types here if needed + }); + } + + // Handle tool calls if present + if (message.toolCalls) { + message.toolCalls.forEach((toolCall) => { + parts.push(fromMistralToolCall(toolCall)); + }); + } + + return parts; +} + +// Maps Mistral finish reasons to Genkit finish reasons +export function fromMistralFinishReason( + reason: ChatCompletionChoiceFinishReason | undefined +): 'length' | 'unknown' | 'stop' | 'blocked' | 'other' { + switch (reason) { + case ChatCompletionChoiceFinishReason.Stop: + return 'stop'; + case ChatCompletionChoiceFinishReason.Length: + case ChatCompletionChoiceFinishReason.ModelLength: + return 'length'; + case ChatCompletionChoiceFinishReason.Error: + return 'other'; // Map generic errors to "other" + case ChatCompletionChoiceFinishReason.ToolCalls: + return 'stop'; // Assuming tool calls signify a "stop" in processing + default: + return 'other'; // For undefined or unmapped reasons + } +} + +// Converts a Mistral response to a Genkit response +export function fromMistralResponse( + _input: GenerateRequest, + response: ChatCompletionResponse +): ModelResponseData { + const firstChoice = response.choices?.[0]; + // Convert content from Mistral response to Genkit parts + const contentParts: Part[] = firstChoice?.message + ? fromMistralMessage(firstChoice.message) + : []; + + const message: MessageData = { + role: 'model', + content: contentParts, + }; + + return { + message, + finishReason: fromMistralFinishReason(firstChoice?.finishReason), + usage: { + inputTokens: response.usage.promptTokens, + outputTokens: response.usage.completionTokens, + }, + custom: { + id: response.id, + model: response.model, + created: response.created, + }, + raw: response, // Include the raw response for debugging or additional context + }; +} + +export function mistralModel( + ai: Genkit, + modelName: string, + projectId: string, + region: string +) { + const getClient = createClientFactory(projectId); + + const model = SUPPORTED_MISTRAL_MODELS[modelName]; + if (!model) { + throw new Error(`Unsupported Mistral model name ${modelName}`); + } + + return ai.defineModel( + { + name: model.name, + label: model.info?.label, + configSchema: MistralConfigSchema, + supports: model.info?.supports, + versions: model.info?.versions, + }, + async (input, streamingCallback) => { + const client = getClient(input.config?.location || region); + + const versionedModel = + input.config?.version ?? model.info?.versions?.[0] ?? model.name; + + if (!streamingCallback) { + const mistralRequest = toMistralRequest(versionedModel, input); + + const response = await client.chat.complete(mistralRequest, { + fetchOptions: { + headers: { + 'X-Goog-Api-Client': GENKIT_CLIENT_HEADER, + }, + }, + }); + + return fromMistralResponse(input, response); + } else { + const mistralRequest = toMistralRequest(versionedModel, input); + const stream = await client.chat.stream(mistralRequest, { + fetchOptions: { + headers: { + 'X-Goog-Api-Client': GENKIT_CLIENT_HEADER, + }, + }, + }); + + for await (const event of stream) { + const parts = fromMistralCompletionChunk(event.data); + if (parts.length > 0) { + streamingCallback({ + content: parts, + }); + } + } + + // Get the complete response after streaming + const completeResponse = await client.chat.complete(mistralRequest, { + fetchOptions: { + headers: { + 'X-Goog-Api-Client': GENKIT_CLIENT_HEADER, + }, + }, + }); + + return fromMistralResponse(input, completeResponse); + } + } + ); +} + +function createClientFactory(projectId: string) { + const clients: Record = {}; + + return (region: string): MistralGoogleCloud => { + if (!region) { + throw new Error('Region is required to create Mistral client'); + } + + try { + if (!clients[region]) { + clients[region] = new MistralGoogleCloud({ + region, + projectId, + }); + } + return clients[region]; + } catch (error) { + throw new Error( + `Failed to create/retrieve Mistral client for region ${region}: ${error}` + ); + } + }; +} + +type MistralMessage = + | AssistantMessage + | ToolMessage + | SystemMessage + | UserMessage; + +// Helper function to validate tool calls and responses match +function validateToolSequence(messages: MistralMessage[]) { + const toolCalls = ( + messages.filter((m) => { + return m.role === 'assistant' && m.toolCalls; + }) as AssistantMessage[] + ).reduce((acc: ToolCall[], m) => { + if (m.toolCalls) { + return [...acc, ...m.toolCalls]; + } + return acc; + }, []); + + const toolResponses = messages.filter( + (m) => m.role === 'tool' + ) as ToolMessage[]; + + if (toolCalls.length !== toolResponses.length) { + throw new Error( + `Mismatch between tool calls (${toolCalls.length}) and responses (${toolResponses.length})` + ); + } + + toolResponses.forEach((response) => { + const matchingCall = toolCalls.find( + (call) => call.id === response.toolCallId + ); + if (!matchingCall) { + throw new Error( + `Tool response with ID ${response.toolCallId} has no matching call` + ); + } + }); +} + +export function fromMistralCompletionChunk(chunk: CompletionChunk): Part[] { + if (!chunk.choices?.[0]?.delta) return []; + + const delta = chunk.choices[0].delta; + const parts: Part[] = []; + + if (typeof delta.content === 'string') { + parts.push({ text: delta.content }); + } + + if (delta.toolCalls) { + delta.toolCalls.forEach((toolCall) => { + if (!toolCall.function) return; + + parts.push({ + toolRequest: { + ref: toolCall.id, + name: toolCall.function.name, + input: + typeof toolCall.function.arguments === 'string' + ? JSON.parse(toolCall.function.arguments) + : toolCall.function.arguments, + }, + }); + }); + } + + return parts; +} diff --git a/js/plugins/vertexai/tests/anthropic_test.ts b/js/plugins/vertexai/tests/modelgarden/anthropic_test.ts similarity index 99% rename from js/plugins/vertexai/tests/anthropic_test.ts rename to js/plugins/vertexai/tests/modelgarden/anthropic_test.ts index dc4328a48..61849b8dc 100644 --- a/js/plugins/vertexai/tests/anthropic_test.ts +++ b/js/plugins/vertexai/tests/modelgarden/anthropic_test.ts @@ -25,7 +25,7 @@ import { AnthropicConfigSchema, fromAnthropicResponse, toAnthropicRequest, -} from '../src/modelgarden/anthropic'; +} from '../../src/modelgarden/anthropic'; const MODEL_ID = 'modelid'; diff --git a/js/plugins/vertexai/tests/modelgarden/mistral_test.ts b/js/plugins/vertexai/tests/modelgarden/mistral_test.ts new file mode 100644 index 000000000..e7b5efef2 --- /dev/null +++ b/js/plugins/vertexai/tests/modelgarden/mistral_test.ts @@ -0,0 +1,688 @@ +/** + * Copyright 2024 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 { + ChatCompletionRequest, + ChatCompletionResponse, + CompletionChunk, +} from '@mistralai/mistralai-gcp/models/components'; +import { GenerateRequest, GenerateResponseData } from 'genkit'; +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { + MistralConfigSchema, + fromMistralCompletionChunk, + fromMistralResponse, + toMistralRequest, +} from '../../src/modelgarden/mistral'; + +const MODEL_ID = 'mistral-large-2411'; + +describe('toMistralRequest', () => { + const testCases: { + should: string; + input: GenerateRequest; + expectedOutput: ChatCompletionRequest; + }[] = [ + { + should: 'should transform genkit message (text content) correctly', + input: { + messages: [ + { + role: 'user' as const, + content: [{ text: 'Tell a joke about dogs.' }], + }, + ], + }, + expectedOutput: { + model: MODEL_ID, + messages: [ + { + role: 'user', + content: 'Tell a joke about dogs.', + }, + ], + maxTokens: 1024, + temperature: 0.7, + }, + }, + { + should: 'should transform system message', + input: { + messages: [ + { + role: 'system' as const, + content: [{ text: 'Talk like a pirate.' }], + }, + { + role: 'user' as const, + content: [{ text: 'Tell a joke about dogs.' }], + }, + ], + }, + expectedOutput: { + model: MODEL_ID, + messages: [ + { + role: 'system', + content: 'Talk like a pirate.', + }, + { + role: 'user', + content: 'Tell a joke about dogs.', + }, + ], + maxTokens: 1024, + temperature: 0.7, + }, + }, + { + should: 'should transform tool request correctly', + input: { + messages: [ + { + role: 'user' as const, + content: [{ text: "What's the weather like today?" }], + }, + ], + tools: [ + { + name: 'get_weather', + description: 'Get the weather for a location.', + inputSchema: { + type: 'object', + properties: { + location: { + type: 'string', + description: 'The city and state, e.g. San Francisco, CA', + }, + }, + required: ['location'], + }, + }, + ], + }, + expectedOutput: { + model: MODEL_ID, + messages: [ + { + role: 'user', + content: "What's the weather like today?", + }, + ], + maxTokens: 1024, + temperature: 0.7, + tools: [ + { + type: 'function', + function: { + name: 'get_weather', + description: 'Get the weather for a location.', + parameters: { + type: 'object', + properties: { + location: { + type: 'string', + description: 'The city and state, e.g. San Francisco, CA', + }, + }, + required: ['location'], + }, + }, + }, + ], + }, + }, + ]; + for (const test of testCases) { + it(test.should, () => { + assert.deepEqual( + toMistralRequest(MODEL_ID, test.input), + test.expectedOutput + ); + }); + } +}); + +describe('fromMistralResponse', () => { + const testCases: { + should: string; + input: GenerateRequest; + response: ChatCompletionResponse; + expectedOutput: GenerateResponseData; + }[] = [ + { + should: 'should transform mistral message (text content) correctly', + input: { + messages: [ + { + role: 'user' as const, + content: [{ text: 'Tell a joke about dogs.' }], + }, + ], + }, + response: { + id: 'abcd1234', + object: 'chat.completion', + model: MODEL_ID, + choices: [ + { + index: 0, + message: { + role: 'assistant', + content: + 'Why do dogs make terrible comedians? Their jokes are too ruff!', + }, + finishReason: 'stop', + }, + ], + usage: { + promptTokens: 123, + completionTokens: 234, + totalTokens: 357, + }, + }, + expectedOutput: { + message: { + role: 'model' as const, + content: [ + { + text: 'Why do dogs make terrible comedians? Their jokes are too ruff!', + }, + ], + }, + finishReason: 'stop', + usage: { + inputTokens: 123, + outputTokens: 234, + }, + custom: { + id: 'abcd1234', + model: MODEL_ID, + created: undefined, + }, + raw: { + id: 'abcd1234', + object: 'chat.completion', + model: MODEL_ID, + choices: [ + { + index: 0, + message: { + role: 'assistant', + content: + 'Why do dogs make terrible comedians? Their jokes are too ruff!', + }, + finishReason: 'stop', + }, + ], + usage: { + promptTokens: 123, + completionTokens: 234, + totalTokens: 357, + }, + }, + }, + }, + { + should: 'should transform tool calls correctly', + input: { + messages: [ + { + role: 'user' as const, + content: [{ text: "What's the weather like today?" }], + }, + ], + }, + response: { + id: 'abcd1234', + object: 'chat.completion', + model: MODEL_ID, + choices: [ + { + index: 0, + message: { + role: 'assistant', + content: null, + toolCalls: [ + { + id: 'call_abc123', + type: 'function', + function: { + name: 'get_weather', + arguments: '{"location":"San Francisco, CA"}', + }, + }, + ], + }, + finishReason: 'tool_calls', + }, + ], + usage: { + promptTokens: 123, + completionTokens: 234, + totalTokens: 357, + }, + }, + expectedOutput: { + message: { + role: 'model' as const, + content: [ + { + toolRequest: { + ref: 'call_abc123', + name: 'get_weather', + input: { location: 'San Francisco, CA' }, + }, + }, + ], + }, + finishReason: 'stop', + usage: { + inputTokens: 123, + outputTokens: 234, + }, + custom: { + id: 'abcd1234', + model: MODEL_ID, + created: undefined, + }, + raw: { + id: 'abcd1234', + object: 'chat.completion', + model: MODEL_ID, + choices: [ + { + index: 0, + message: { + role: 'assistant', + content: null, + toolCalls: [ + { + id: 'call_abc123', + type: 'function', + function: { + name: 'get_weather', + arguments: '{"location":"San Francisco, CA"}', + }, + }, + ], + }, + finishReason: 'tool_calls', + }, + ], + usage: { + promptTokens: 123, + completionTokens: 234, + totalTokens: 357, + }, + }, + }, + }, + ]; + for (const test of testCases) { + it(test.should, () => { + assert.deepEqual( + fromMistralResponse(test.input, test.response), + test.expectedOutput + ); + }); + } +}); + +describe('validateToolSequence', () => { + it('should handle valid tool call and response sequence', () => { + const input = { + messages: [ + { + role: 'user' as const, + content: [{ text: "What's the weather?" }], + }, + { + role: 'model' as const, + content: [ + { + toolRequest: { + ref: 'call_123', + name: 'get_weather', + input: { location: 'San Francisco' }, + }, + }, + ], + }, + { + role: 'tool' as const, + content: [ + { + toolResponse: { + ref: 'call_123', + name: 'get_weather', + output: { temperature: 72, condition: 'sunny' }, + }, + }, + ], + }, + ], + }; + + // Should not throw an error + assert.doesNotThrow(() => toMistralRequest(MODEL_ID, input)); + }); + + it('should throw error when tool response is missing', () => { + const input = { + messages: [ + { + role: 'user' as const, + content: [{ text: "What's the weather?" }], + }, + { + role: 'model' as const, + content: [ + { + toolRequest: { + ref: 'call_123', + name: 'get_weather', + input: { location: 'San Francisco' }, + }, + }, + ], + }, + ], + }; + + assert.throws( + () => toMistralRequest(MODEL_ID, input), + /Mismatch between tool calls/ + ); + }); + + it('should throw error when tool response id does not match call', () => { + const input = { + messages: [ + { + role: 'user' as const, + content: [{ text: "What's the weather?" }], + }, + { + role: 'model' as const, + content: [ + { + toolRequest: { + ref: 'call_123', + name: 'get_weather', + input: { location: 'San Francisco' }, + }, + }, + ], + }, + { + role: 'tool' as const, + content: [ + { + toolResponse: { + ref: 'wrong_id', + name: 'get_weather', + output: { temperature: 72, condition: 'sunny' }, + }, + }, + ], + }, + ], + }; + + assert.throws( + () => toMistralRequest(MODEL_ID, input), + /Tool response with ID wrong_id has no matching call/ + ); + }); +}); + +describe('edge cases', () => { + it('should handle empty message content', () => { + const input = { + messages: [ + { + role: 'user' as const, + content: [], + }, + ], + }; + + const result = toMistralRequest(MODEL_ID, input); + assert.equal(result.messages[0].content, ''); + }); + + it('should handle multiple text parts in content', () => { + const input = { + messages: [ + { + role: 'user' as const, + content: [{ text: 'Hello' }, { text: ' ' }, { text: 'World' }], + }, + ], + }; + + const result = toMistralRequest(MODEL_ID, input); + assert.equal(result.messages[0].content, 'Hello World'); + }); + + it('should handle custom configuration options', () => { + const input = { + messages: [ + { + role: 'user' as const, + content: [{ text: 'test' }], + }, + ], + config: { + temperature: 0.5, + maxOutputTokens: 500, + topP: 0.9, + stopSequences: ['END'], + }, + }; + + const result = toMistralRequest(MODEL_ID, input); + assert.equal(result.temperature, 0.5); + assert.equal(result.maxTokens, 500); + assert.equal(result.topP, 0.9); + assert.deepEqual(result.stop, ['END']); + }); +}); + +describe('fromMistralResponse error handling', () => { + it('should handle response with no choices', () => { + const input = { + messages: [ + { + role: 'user' as const, + content: [{ text: 'test' }], + }, + ], + }; + + const response = { + id: 'test', + object: 'chat.completion', + model: MODEL_ID, + choices: [], + usage: { + promptTokens: 0, + completionTokens: 0, + totalTokens: 0, + }, + }; + + const result = fromMistralResponse(input, response); + assert.deepEqual(result.message!.content, []); + }); +}); + +describe('fromMistralCompletionChunk', () => { + it('should handle text content chunk', () => { + const chunk = { + id: 'chunk_1', + model: MODEL_ID, + choices: [ + { + index: 0, + delta: { + role: 'assistant', + content: 'Hello world', + }, + }, + ], + }; + + const parts = fromMistralCompletionChunk( + chunk as unknown as CompletionChunk + ); + assert.deepEqual(parts, [{ text: 'Hello world' }]); + }); + + it('should handle tool call chunk', () => { + const chunk = { + id: 'chunk_1', + model: MODEL_ID, + choices: [ + { + index: 0, + delta: { + role: 'assistant', + toolCalls: [ + { + id: 'call_123', + type: 'function', + function: { + name: 'get_weather', + arguments: '{"location":"San Francisco"}', + }, + }, + ], + }, + }, + ], + }; + + const parts = fromMistralCompletionChunk( + chunk as unknown as CompletionChunk + ); + assert.deepEqual(parts, [ + { + toolRequest: { + ref: 'call_123', + name: 'get_weather', + input: { location: 'San Francisco' }, + }, + }, + ]); + }); + + it('should handle empty chunk', () => { + const chunk = { + id: 'chunk_1', + model: MODEL_ID, + choices: [], + }; + + const parts = fromMistralCompletionChunk(chunk); + assert.deepEqual(parts, []); + }); + + it('should handle chunk with empty delta', () => { + const chunk = { + id: 'chunk_1', + model: MODEL_ID, + choices: [ + { + index: 0, + delta: {}, + }, + ], + }; + + const parts = fromMistralCompletionChunk( + chunk as unknown as CompletionChunk + ); + assert.deepEqual(parts, []); + }); + + it('should handle chunk with invalid tool call', () => { + const chunk = { + id: 'chunk_1', + model: MODEL_ID, + choices: [ + { + index: 0, + delta: { + toolCalls: [ + { + id: 'call_123', + type: 'function', + // Missing function property + }, + ], + }, + }, + ], + }; + + const parts = fromMistralCompletionChunk( + chunk as unknown as CompletionChunk + ); + assert.deepEqual(parts, []); // Should skip invalid tool call + }); + + it('should handle mixed content chunk', () => { + const chunk = { + id: 'chunk_1', + model: MODEL_ID, + choices: [ + { + index: 0, + delta: { + content: 'Some text', + toolCalls: [ + { + id: 'call_123', + type: 'function', + function: { + name: 'get_weather', + arguments: '{"location":"San Francisco"}', + }, + }, + ], + }, + }, + ], + }; + + const parts = fromMistralCompletionChunk( + chunk as unknown as CompletionChunk + ); + assert.deepEqual(parts, [ + { text: 'Some text' }, + { + toolRequest: { + ref: 'call_123', + name: 'get_weather', + input: { location: 'San Francisco' }, + }, + }, + ]); + }); +}); diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 16bdcc101..c290ca349 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -717,6 +717,9 @@ importers: '@google-cloud/vertexai': specifier: ^1.9.0 version: 1.9.0(encoding@0.1.13) + '@mistralai/mistralai-gcp': + specifier: ^1.3.5 + version: 1.3.5(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.23.8) genkit: specifier: workspace:* version: link:../../genkit @@ -759,28 +762,6 @@ importers: specifier: ^4.9.0 version: 4.9.5 - testapps/anthropic-models: - dependencies: - '@genkit-ai/firebase': - specifier: workspace:* - version: link:../../plugins/firebase - '@genkit-ai/vertexai': - specifier: workspace:* - version: link:../../plugins/vertexai - express: - specifier: ^4.21.0 - version: 4.21.0 - genkit: - specifier: workspace:* - version: link:../../genkit - zod: - specifier: 3.22.4 - version: 3.22.4 - devDependencies: - typescript: - specifier: ^5.5.3 - version: 5.5.3 - testapps/basic-gemini: dependencies: '@genkit-ai/firebase': @@ -1245,6 +1226,31 @@ importers: specifier: ^5.3.3 version: 5.4.5 + testapps/mistral-models: + dependencies: + '@genkit-ai/firebase': + specifier: workspace:* + version: link:../../plugins/firebase + '@genkit-ai/vertexai': + specifier: workspace:* + version: link:../../plugins/vertexai + '@mistralai/mistralai-gcp': + specifier: ^1.3.4 + version: 1.3.4(zod@3.22.4) + express: + specifier: ^4.21.0 + version: 4.21.0 + genkit: + specifier: workspace:* + version: link:../../genkit + zod: + specifier: 3.22.4 + version: 3.22.4 + devDependencies: + typescript: + specifier: ^5.5.3 + version: 5.6.3 + testapps/model-tester: dependencies: '@genkit-ai/googleai': @@ -1267,7 +1273,7 @@ importers: version: link:../../plugins/ollama genkitx-openai: specifier: ^0.10.1 - version: 0.10.1(@genkit-ai/ai@0.9.5)(@genkit-ai/core@0.9.5) + version: 0.10.1(@genkit-ai/ai@0.9.6)(@genkit-ai/core@0.9.6) devDependencies: rimraf: specifier: ^6.0.1 @@ -1366,6 +1372,31 @@ importers: specifier: ^5.3.3 version: 5.4.5 + testapps/vertexai-modelgarden: + dependencies: + '@genkit-ai/firebase': + specifier: workspace:* + version: link:../../plugins/firebase + '@genkit-ai/vertexai': + specifier: workspace:* + version: link:../../plugins/vertexai + '@mistralai/mistralai-gcp': + specifier: ^1.3.4 + version: 1.3.4(zod@3.22.4) + express: + specifier: ^4.21.0 + version: 4.21.0 + genkit: + specifier: workspace:* + version: link:../../genkit + zod: + specifier: 3.22.4 + version: 3.22.4 + devDependencies: + typescript: + specifier: ^5.5.3 + version: 5.6.3 + testapps/vertexai-reranker: dependencies: '@genkit-ai/dev-local-vectorstore': @@ -2056,11 +2087,11 @@ packages: '@firebase/util@1.9.5': resolution: {integrity: sha512-PP4pAFISDxsf70l3pEy34Mf3GkkUcVQ3MdKp6aSVb7tcpfUQxnsdV7twDd8EkfB6zZylH6wpUAoangQDmCUMqw==} - '@genkit-ai/ai@0.9.5': - resolution: {integrity: sha512-C1YzQILR3t1U7ZQSsliv8bC5zhufSw7S1rAb8OJN83m4pu2dUQLgUk0KoT1Iu+61Vtg48U+7MFLvVkhwUHM5Xg==} + '@genkit-ai/ai@0.9.6': + resolution: {integrity: sha512-GiNK8LmCIs/sU6TgVG6GxRpYcIerJdXE649RJsjJQb5pTajOGUYTaVB14nWT4CBKsW2UseSembe0oG64gmxCDg==} - '@genkit-ai/core@0.9.5': - resolution: {integrity: sha512-iP7uF/try5RBzn5tS+UOEz0IdkAenE5FoWUl+j4lPVqij8PePjXQROGaTAjA+nIyRCrjHq2hLPjZ4kO9mmtZ7w==} + '@genkit-ai/core@0.9.6': + resolution: {integrity: sha512-VzcNkVNFSTYNI39bQPAN11QbtZNHuCVxUPcAR0pGAcY6SpqjRSa21WxdLn3dDNjP6sv+pOZdJX3VFln2q5krvg==} '@google-cloud/aiplatform@3.25.0': resolution: {integrity: sha512-qKnJgbyCENjed8e1G5zZGFTxxNKhhaKQN414W2KIVHrLxMFmlMuG+3QkXPOWwXBnT5zZ7aMxypt5og0jCirpHg==} @@ -2566,6 +2597,18 @@ packages: '@material/material-color-utilities@0.2.7': resolution: {integrity: sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==} + '@mistralai/mistralai-gcp@1.3.4': + resolution: {integrity: sha512-tMWTA8r+MX+/hDn3n1mo8WY/NWN4AJdvwShdgsThrt+bwfwhSlorrz7bH04UFxob13GJ/XsRC3iUg7EAWH0BoQ==} + peerDependencies: + zod: '>= 3' + + '@mistralai/mistralai-gcp@1.3.5': + resolution: {integrity: sha512-eykxLojLv0AcgGui2D+D/S98Toc18j9g0GCvjyah3E8YtQW0dMb6UyQjmB+2+qDXN3OZjp8+dOkoJ+r7DmwbOQ==} + peerDependencies: + react: ^18 || ^19 + react-dom: ^18 || ^19 + zod: '>= 3' + '@modelcontextprotocol/sdk@1.0.0': resolution: {integrity: sha512-mbe0otw8vTtZoL5pVucXAmx6oEC7YjdXBgVeFkJXASu4OAnLkrIeNw9zwzU5CwEp19M54bjOUGcna90Dl/H5Bw==} @@ -5032,6 +5075,10 @@ packages: long@5.2.3: resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + lru-cache@10.2.0: resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} engines: {node: 14 || >=16.14} @@ -5603,9 +5650,18 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + read-pkg@3.0.0: resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} engines: {node: '>=4'} @@ -5698,6 +5754,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true @@ -6805,9 +6864,9 @@ snapshots: dependencies: tslib: 2.6.2 - '@genkit-ai/ai@0.9.5': + '@genkit-ai/ai@0.9.6': dependencies: - '@genkit-ai/core': 0.9.5 + '@genkit-ai/core': 0.9.6 '@opentelemetry/api': 1.9.0 '@types/node': 20.16.9 colorette: 2.0.20 @@ -6818,7 +6877,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@genkit-ai/core@0.9.5': + '@genkit-ai/core@0.9.6': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) @@ -7293,6 +7352,24 @@ snapshots: '@material/material-color-utilities@0.2.7': {} + '@mistralai/mistralai-gcp@1.3.4(zod@3.22.4)': + dependencies: + google-auth-library: 9.14.2(encoding@0.1.13) + zod: 3.22.4 + transitivePeerDependencies: + - encoding + - supports-color + + '@mistralai/mistralai-gcp@1.3.5(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.23.8)': + dependencies: + google-auth-library: 9.14.2(encoding@0.1.13) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zod: 3.23.8 + transitivePeerDependencies: + - encoding + - supports-color + '@modelcontextprotocol/sdk@1.0.0': dependencies: content-type: 1.0.5 @@ -9279,10 +9356,10 @@ snapshots: - encoding - supports-color - genkitx-openai@0.10.1(@genkit-ai/ai@0.9.5)(@genkit-ai/core@0.9.5): + genkitx-openai@0.10.1(@genkit-ai/ai@0.9.6)(@genkit-ai/core@0.9.6): dependencies: - '@genkit-ai/ai': 0.9.5 - '@genkit-ai/core': 0.9.5 + '@genkit-ai/ai': 0.9.6 + '@genkit-ai/core': 0.9.6 openai: 4.53.0(encoding@0.1.13) zod: 3.23.8 transitivePeerDependencies: @@ -10422,6 +10499,10 @@ snapshots: long@5.2.3: {} + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + lru-cache@10.2.0: {} lru-cache@11.0.1: {} @@ -11020,8 +11101,18 @@ snapshots: strip-json-comments: 2.0.1 optional: true + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + react-is@18.3.1: {} + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + read-pkg@3.0.0: dependencies: load-json-file: 4.0.0 @@ -11140,6 +11231,10 @@ snapshots: safer-buffer@2.1.2: {} + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + semver@5.7.2: {} semver@6.3.1: {} diff --git a/js/testapps/anthropic-models/package.json b/js/testapps/vertexai-modelgarden/package.json similarity index 79% rename from js/testapps/anthropic-models/package.json rename to js/testapps/vertexai-modelgarden/package.json index e2b70fbef..50260e8d4 100644 --- a/js/testapps/anthropic-models/package.json +++ b/js/testapps/vertexai-modelgarden/package.json @@ -1,7 +1,8 @@ { "main": "lib/index.js", "scripts": { - "start": "node lib/index.js", + "start:anthropic": "node lib/anthropic.js", + "start:mistral": "node lib/mistral.js", "build": "tsc", "build:watch": "tsc --watch", "test": "echo \"Error: no test specified\" && exit 1" @@ -13,10 +14,11 @@ "author": "", "license": "ISC", "dependencies": { - "genkit": "workspace:*", "@genkit-ai/firebase": "workspace:*", "@genkit-ai/vertexai": "workspace:*", + "@mistralai/mistralai-gcp": "^1.3.4", "express": "^4.21.0", + "genkit": "workspace:*", "zod": "3.22.4" }, "devDependencies": { diff --git a/js/testapps/anthropic-models/src/index.ts b/js/testapps/vertexai-modelgarden/src/anthropic.ts similarity index 100% rename from js/testapps/anthropic-models/src/index.ts rename to js/testapps/vertexai-modelgarden/src/anthropic.ts diff --git a/js/testapps/vertexai-modelgarden/src/mistral.ts b/js/testapps/vertexai-modelgarden/src/mistral.ts new file mode 100644 index 000000000..19ce217ae --- /dev/null +++ b/js/testapps/vertexai-modelgarden/src/mistral.ts @@ -0,0 +1,107 @@ +/** + * Copyright 2024 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 { vertexAI } from '@genkit-ai/vertexai'; +import { + codestral, + mistralLarge, + mistralNemo, + vertexAIModelGarden, +} from '@genkit-ai/vertexai/modelgarden'; +import { genkit, z } from 'genkit'; + +const ai = genkit({ + plugins: [ + vertexAI({ + location: 'europe-west4', + }), + vertexAIModelGarden({ + location: 'europe-west4', + models: [mistralLarge, mistralNemo, codestral], + }), + ], +}); + +// Mistral Nemo for quick validation and analysis +export const analyzeCode = ai.defineFlow( + { + name: 'analyzeCode', + inputSchema: z.object({ + code: z.string(), + }), + outputSchema: z.string(), + }, + async ({ code }) => { + const analysis = await ai.generate({ + model: mistralNemo, + prompt: `Analyze this code for potential issues and suggest improvements: + ${code}`, + }); + + return analysis.text; + } +); + +// Codestral for code generation +export const generateFunction = ai.defineFlow( + { + name: 'generateFunction', + inputSchema: z.object({ + description: z.string(), + }), + outputSchema: z.string(), + }, + async ({ description }) => { + const result = await ai.generate({ + model: codestral, + prompt: `Create a TypeScript function that ${description}. Include error handling and types.`, + }); + + return result.text; + } +); + +// Mistral Large for detailed explanations +export const explainConcept = ai.defineFlow( + { + name: 'explainConcept', + inputSchema: z.object({ + concept: z.string(), + }), + outputSchema: z.object({ + explanation: z.string(), + examples: z.array(z.string()), + }), + }, + async ({ concept }) => { + const explanation = await ai.generate({ + model: mistralLarge, + prompt: `Explain ${concept} in programming. Include practical examples.`, + config: { + version: 'mistral-large-2407', + temperature: 0.7, + }, + output: { + schema: z.object({ + explanation: z.string(), + examples: z.array(z.string()), + }), + }, + }); + + return explanation.output || { explanation: '', examples: [] }; + } +); diff --git a/js/testapps/anthropic-models/tsconfig.json b/js/testapps/vertexai-modelgarden/tsconfig.json similarity index 100% rename from js/testapps/anthropic-models/tsconfig.json rename to js/testapps/vertexai-modelgarden/tsconfig.json From f69dac38f1d4662f1e7eec58a6b7015e7bb10d21 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Wed, 4 Dec 2024 15:50:31 -0500 Subject: [PATCH 006/562] feat(js/flows): consolidated `defineFlow` and `defineStreamingFlow` (#1401) --- js/core/src/flow.ts | 67 +++++++++++++--------- js/core/tests/flow_test.ts | 107 ++++++++++++++++++++++++++++++++++- js/core/tests/utils.ts | 60 ++++++++++++++++++++ js/genkit/src/genkit.ts | 9 ++- js/genkit/tests/flow_test.ts | 81 ++++++++++++++++++++++++++ 5 files changed, 293 insertions(+), 31 deletions(-) create mode 100644 js/core/tests/utils.ts create mode 100644 js/genkit/tests/flow_test.ts diff --git a/js/core/src/flow.ts b/js/core/src/flow.ts index 6a9948be6..60d193146 100644 --- a/js/core/src/flow.ts +++ b/js/core/src/flow.ts @@ -91,32 +91,37 @@ export interface StreamingFlowConfig< streamSchema?: S; } +export interface FlowCallOptions { + /** @deprecated use {@link context} instead. */ + withLocalAuthContext?: unknown; + context?: unknown; +} + /** * Non-streaming flow that can be called directly like a function. */ export interface CallableFlow< I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, + S extends z.ZodTypeAny = z.ZodTypeAny, > { - ( - input?: z.infer, - opts?: { withLocalAuthContext?: unknown } - ): Promise>; + (input?: z.infer, opts?: FlowCallOptions): Promise>; + + stream(input?: z.infer, opts?: FlowCallOptions): StreamingResponse; + flow: Flow; } /** * Streaming flow that can be called directly like a function. + * @deprecated use {@link CallableFlow} */ export interface StreamableFlow< I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny, > { - ( - input?: z.infer, - opts?: { withLocalAuthContext?: unknown } - ): StreamingResponse; + (input?: z.infer, opts?: FlowCallOptions): StreamingResponse; flow: Flow; } @@ -128,7 +133,7 @@ interface StreamingResponse< S extends z.ZodTypeAny = z.ZodTypeAny, > { /** Iterator over the streaming chunks. */ - stream: AsyncGenerator, z.infer | undefined>; + stream: AsyncGenerator>; /** Final output of the flow. */ output: Promise>; } @@ -144,7 +149,7 @@ export type FlowFn< /** Input to the flow. */ input: z.infer, /** Callback for streaming functions only. */ - streamingCallback?: StreamingCallback> + streamingCallback: StreamingCallback> ) => Promise> | z.infer; /** @@ -223,7 +228,10 @@ export class Flow< }); try { metadata.input = input; - const output = await this.flowFn(input, opts.streamingCallback); + const output = await this.flowFn( + input, + opts.streamingCallback ?? (() => {}) + ); metadata.output = JSON.stringify(output); setCustomMetadataAttribute(flowMetadataPrefix('state'), 'done'); return { @@ -252,10 +260,7 @@ export class Flow< /** * Runs the flow. This is used when calling a flow from another flow. */ - async run( - payload?: z.infer, - opts?: { withLocalAuthContext?: unknown } - ): Promise> { + async run(payload?: z.infer, opts?: FlowCallOptions): Promise> { const input = this.inputSchema ? this.inputSchema.parse(payload) : payload; await this.authPolicy?.(opts?.withLocalAuthContext, payload); @@ -266,7 +271,7 @@ export class Flow< } const result = await this.invoke(input, { - auth: opts?.withLocalAuthContext, + auth: opts?.context || opts?.withLocalAuthContext, }); return result.result; } @@ -276,7 +281,7 @@ export class Flow< */ stream( payload?: z.infer, - opts?: { withLocalAuthContext?: unknown } + opts?: FlowCallOptions ): StreamingResponse { let chunkStreamController: ReadableStreamController>; const chunkStream = new ReadableStream>({ @@ -288,7 +293,7 @@ export class Flow< }); const authPromise = - this.authPolicy?.(opts?.withLocalAuthContext, payload) ?? + this.authPolicy?.(opts?.context || opts?.withLocalAuthContext, payload) ?? Promise.resolve(); const invocationPromise = authPromise @@ -301,7 +306,7 @@ export class Flow< }) as S extends z.ZodVoid ? undefined : StreamingCallback>, - auth: opts?.withLocalAuthContext, + auth: opts?.context || opts?.withLocalAuthContext, } ).then((s) => s.result) ) @@ -530,21 +535,31 @@ export class FlowServer { export function defineFlow< I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, + S extends z.ZodTypeAny = z.ZodTypeAny, >( registry: Registry, - config: FlowConfig | string, - fn: FlowFn -): CallableFlow { + config: StreamingFlowConfig | string, + fn: FlowFn +): CallableFlow { const resolvedConfig: FlowConfig = typeof config === 'string' ? { name: config } : config; - const flow = new Flow(registry, resolvedConfig, fn); + const flow = new Flow(registry, resolvedConfig, fn); registerFlowAction(registry, flow); - const callableFlow: CallableFlow = async (input, opts) => { + const callableFlow = async ( + input: z.infer, + opts: FlowCallOptions + ): Promise> => { return flow.run(input, opts); }; - callableFlow.flow = flow; - return callableFlow; + (callableFlow as CallableFlow).flow = flow; + (callableFlow as CallableFlow).stream = ( + input: z.infer, + opts: FlowCallOptions + ): StreamingResponse => { + return flow.stream(input, opts); + }; + return callableFlow as CallableFlow; } /** diff --git a/js/core/tests/flow_test.ts b/js/core/tests/flow_test.ts index 141e244b1..5a3df6e7f 100644 --- a/js/core/tests/flow_test.ts +++ b/js/core/tests/flow_test.ts @@ -14,11 +14,19 @@ * limitations under the License. */ +import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; -import { defineFlow, defineStreamingFlow } from '../src/flow.js'; -import { getFlowAuth, z } from '../src/index.js'; +import { defineFlow, defineStreamingFlow, run } from '../src/flow.js'; +import { defineAction, getFlowAuth, z } from '../src/index.js'; import { Registry } from '../src/registry.js'; +import { enableTelemetry } from '../src/tracing.js'; +import { TestSpanExporter } from './utils.js'; + +const spanExporter = new TestSpanExporter(); +enableTelemetry({ + spanProcessors: [new SimpleSpanProcessor(spanExporter)], +}); function createTestFlow(registry: Registry) { return defineFlow( @@ -224,4 +232,99 @@ describe('flow', () => { assert.deepEqual(gotChunks, [{ count: 0 }, { count: 1 }, { count: 2 }]); }); }); + + describe('telemetry', async () => { + beforeEach(() => { + spanExporter.exportedSpans = []; + }); + + it('should create a trace', async () => { + const testFlow = createTestFlow(registry); + + const result = await testFlow('foo'); + + assert.equal(result, 'bar foo'); + assert.strictEqual(spanExporter.exportedSpans.length, 1); + assert.strictEqual(spanExporter.exportedSpans[0].displayName, 'testFlow'); + assert.deepStrictEqual(spanExporter.exportedSpans[0].attributes, { + 'genkit:input': '"foo"', + 'genkit:isRoot': true, + 'genkit:metadata:flow:name': 'testFlow', + 'genkit:metadata:flow:state': 'done', + 'genkit:name': 'testFlow', + 'genkit:output': '"bar foo"', + 'genkit:path': '/{testFlow,t:flow}', + 'genkit:state': 'success', + 'genkit:type': 'flow', + }); + }); + + it('records traces of nested actions', async () => { + const testAction = defineAction( + registry, + { + name: 'testAction', + actionType: 'tool', + metadata: { type: 'tool' }, + }, + async (i) => { + return 'bar'; + } + ); + + const testFlow = defineFlow( + registry, + { + name: 'testFlow', + inputSchema: z.string(), + outputSchema: z.string(), + }, + async (input) => { + return run('custom', async () => { + return 'foo ' + (await testAction(undefined)); + }); + } + ); + const result = await testFlow('foo'); + + assert.equal(result, 'foo bar'); + assert.strictEqual(spanExporter.exportedSpans.length, 3); + + assert.strictEqual( + spanExporter.exportedSpans[0].displayName, + 'testAction' + ); + assert.deepStrictEqual(spanExporter.exportedSpans[0].attributes, { + 'genkit:metadata:subtype': 'tool', + 'genkit:name': 'testAction', + 'genkit:output': '"bar"', + 'genkit:path': + '/{testFlow,t:flow}/{custom,t:flowStep}/{testAction,t:action,s:tool}', + 'genkit:state': 'success', + 'genkit:type': 'action', + }); + + assert.strictEqual(spanExporter.exportedSpans[1].displayName, 'custom'); + assert.deepStrictEqual(spanExporter.exportedSpans[1].attributes, { + 'genkit:name': 'custom', + 'genkit:output': '"foo bar"', + 'genkit:path': '/{testFlow,t:flow}/{custom,t:flowStep}', + 'genkit:state': 'success', + 'genkit:type': 'flowStep', + }); + + assert.strictEqual(spanExporter.exportedSpans[2].displayName, 'testFlow'); + assert.deepStrictEqual(spanExporter.exportedSpans[2].attributes, { + 'genkit:input': '"foo"', + 'genkit:isRoot': true, + 'genkit:metadata:flow:name': 'testFlow', + 'genkit:metadata:flow:state': 'done', + 'genkit:name': 'testFlow', + 'genkit:output': '"foo bar"', + 'genkit:path': '/{testFlow,t:flow}', + 'genkit:state': 'success', + 'genkit:type': 'flow', + }); + }); + }); }); diff --git a/js/core/tests/utils.ts b/js/core/tests/utils.ts new file mode 100644 index 000000000..72470e7f0 --- /dev/null +++ b/js/core/tests/utils.ts @@ -0,0 +1,60 @@ +/** + * Copyright 2024 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 { SpanKind } from '@opentelemetry/api'; +import { ExportResult } from '@opentelemetry/core'; +import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base'; + +export class TestSpanExporter implements SpanExporter { + exportedSpans: any[] = []; + + export( + spans: ReadableSpan[], + resultCallback: (result: ExportResult) => void + ): void { + this.exportedSpans.push(...spans.map((s) => this._exportInfo(s))); + resultCallback({ code: 0 }); + } + + shutdown(): Promise { + return this.forceFlush(); + } + + private _exportInfo(span: ReadableSpan) { + return { + spanId: span.spanContext().spanId, + traceId: span.spanContext().traceId, + attributes: { ...span.attributes }, + displayName: span.name, + links: span.links, + spanKind: SpanKind[span.kind], + parentSpanId: span.parentSpanId, + sameProcessAsParentSpan: { value: !span.spanContext().isRemote }, + status: span.status, + timeEvents: { + timeEvent: span.events.map((e) => ({ + annotation: { + attributes: e.attributes ?? {}, + description: e.name, + }, + })), + }, + }; + } + forceFlush(): Promise { + return Promise.resolve(); + } +} diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index fa05208c4..e091ca222 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -110,7 +110,6 @@ import { defineSchema, defineStreamingFlow, Flow, - FlowConfig, FlowFn, FlowServer, FlowServerOptions, @@ -203,7 +202,11 @@ export class Genkit { defineFlow< I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, - >(config: FlowConfig | string, fn: FlowFn): CallableFlow { + S extends z.ZodTypeAny = z.ZodTypeAny, + >( + config: StreamingFlowConfig | string, + fn: FlowFn + ): CallableFlow { const flow = defineFlow(this.registry, config, fn); this.registeredFlows.push(flow.flow); return flow; @@ -212,7 +215,7 @@ export class Genkit { /** * Defines and registers a streaming flow. * - * @todo TODO: Improve this documentation (show snippetss, etc). + * @deprecated use {@link defineFlow} */ defineStreamingFlow< I extends z.ZodTypeAny = z.ZodTypeAny, diff --git a/js/genkit/tests/flow_test.ts b/js/genkit/tests/flow_test.ts new file mode 100644 index 000000000..714b5b111 --- /dev/null +++ b/js/genkit/tests/flow_test.ts @@ -0,0 +1,81 @@ +/** + * Copyright 2024 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 { z } from '@genkit-ai/core'; +import assert from 'node:assert'; +import { beforeEach, describe, it } from 'node:test'; +import { Genkit, genkit } from '../src/genkit'; + +describe('flow', () => { + let ai: Genkit; + + beforeEach(() => { + ai = genkit({}); + }); + + it('calls simple flow', async () => { + const bananaFlow = ai.defineFlow('banana', () => 'banana'); + + assert.strictEqual(await bananaFlow(), 'banana'); + }); + + it('streams simple chunks (no schema defined)', async () => { + const streamingBananaFlow = ai.defineFlow( + 'banana', + (input: string, sendChunk) => { + for (let i = 0; i < input.length; i++) { + sendChunk(input.charAt(i)); + } + return input; + } + ); + + const { stream, output } = streamingBananaFlow.stream('banana'); + let chunks: string[] = []; + for await (const chunk of stream) { + chunks.push(chunk as string); + } + assert.strictEqual(await output, 'banana'); + assert.deepStrictEqual(chunks, ['b', 'a', 'n', 'a', 'n', 'a']); + }); + + it('streams simple chunks with schema defined', async () => { + const streamingBananaFlow = ai.defineFlow( + { + name: 'banana', + inputSchema: z.string(), + streamSchema: z.string(), + }, + (input, sendChunk) => { + for (let i = 0; i < input.length; i++) { + sendChunk(input.charAt(i)); + } + return input; + } + ); + + const { stream, output } = streamingBananaFlow.stream('banana'); + let chunks: string[] = []; + for await (const chunk of stream) { + chunks.push(chunk); + } + assert.deepStrictEqual(chunks, ['b', 'a', 'n', 'a', 'n', 'a']); + assert.strictEqual(await output, 'banana'); + + // a "streaming" flow can be invoked in non-streaming mode. + assert.strictEqual(await streamingBananaFlow('banana2'), 'banana2'); + }); +}); From ce90e00901dd2caaaba9f34e66e5b4b5771eebd5 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 5 Dec 2024 10:43:33 -0500 Subject: [PATCH 007/562] chore: update release_main.sh --- scripts/release_main.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release_main.sh b/scripts/release_main.sh index bc5e824b1..f9b2bf970 100755 --- a/scripts/release_main.sh +++ b/scripts/release_main.sh @@ -7,7 +7,7 @@ # pnpm test:all # Run from root: scripts/release_main.sh -pnpm login --registry https://wombat-dressing-room.appspot.com +# pnpm login --registry https://wombat-dressing-room.appspot.com CURRENT=`pwd` From 26805fddd9dc449989bd81968c155170d49e6b06 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 5 Dec 2024 11:25:19 -0500 Subject: [PATCH 008/562] chore: update pnpm lock file (#1457) --- js/pnpm-lock.yaml | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index c290ca349..c243ed92e 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -1226,31 +1226,6 @@ importers: specifier: ^5.3.3 version: 5.4.5 - testapps/mistral-models: - dependencies: - '@genkit-ai/firebase': - specifier: workspace:* - version: link:../../plugins/firebase - '@genkit-ai/vertexai': - specifier: workspace:* - version: link:../../plugins/vertexai - '@mistralai/mistralai-gcp': - specifier: ^1.3.4 - version: 1.3.4(zod@3.22.4) - express: - specifier: ^4.21.0 - version: 4.21.0 - genkit: - specifier: workspace:* - version: link:../../genkit - zod: - specifier: 3.22.4 - version: 3.22.4 - devDependencies: - typescript: - specifier: ^5.5.3 - version: 5.6.3 - testapps/model-tester: dependencies: '@genkit-ai/googleai': From 2da1a925382aa9dc6233d47c378aed6fd86f5f62 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:28:25 +0000 Subject: [PATCH 009/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@0.9.7 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index dbcfd9473..21f078a24 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "0.9.6", + "version": "0.9.7", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From d73088bc28a4448660d8fd72a1e6498080eb8c1f Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:28:28 +0000 Subject: [PATCH 010/562] chore: bump genkit-cli version to genkit-cli@0.9.7 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 33c0688eb..09a3bf78e 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "0.9.6", + "version": "0.9.7", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From d19726f0a2529c45c915db2f4d7f11e66389c892 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:28:32 +0000 Subject: [PATCH 011/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@0.9.7 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index 202d51839..218029726 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From a31535012c00f2ada2b1ddcf17b07656626dde9a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:34:04 +0000 Subject: [PATCH 012/562] chore: bump @genkit-ai/core version to @genkit-ai/core@0.9.7 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 9c68715a4..95dbcbd9d 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From 8f48c5e3cccd6ad24b5d9eb8966adf06b1f1231b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:34:06 +0000 Subject: [PATCH 013/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@0.9.7 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index 0a8bb1420..ba07730a2 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From bd53b34cef3a03b52aa89b14a10537676326e64e Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:34:09 +0000 Subject: [PATCH 014/562] chore: bump genkit version to genkit@0.9.7 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index e48f92936..3a6cd588f 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From ec009bf4f3ec2f76b3ee1ca3a4cc76c538a78b0c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:36:17 +0000 Subject: [PATCH 015/562] chore: bump genkitx-chromadb version to genkitx-chromadb0.9.7 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index e0e8c6a5d..bf41f3d5f 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From ab7d6b4ab5f44f8435051a8ff5f343332a5fefe6 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:36:20 +0000 Subject: [PATCH 016/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@0.9.7 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 423a17368..6f82bd7dd 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From 4ee56fba3876a1214dcbb5ec2b263fda325cab41 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:36:22 +0000 Subject: [PATCH 017/562] chore: bump @genkit-ai/dotprompt version to @genkit-ai/dotprompt@0.9.7 --- js/plugins/dotprompt/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dotprompt/package.json b/js/plugins/dotprompt/package.json index 660fa4dc5..2d6762313 100644 --- a/js/plugins/dotprompt/package.json +++ b/js/plugins/dotprompt/package.json @@ -9,7 +9,7 @@ "prompting", "templating" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From f35d7950e64eff0f3935f3732937f6ab83c423e9 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:36:25 +0000 Subject: [PATCH 018/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@0.9.7 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index a4df4f7a9..3e45ded09 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From 09392c0dd4c78b54d3a62c92419e421e63eb4d18 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:36:27 +0000 Subject: [PATCH 019/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@0.9.7 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 73e6bae7e..8139c2d6a 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From c07ffd1282ca91bbfcac7b930d214e027c59ad65 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:36:30 +0000 Subject: [PATCH 020/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@0.9.7 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index 40e571f07..3e8097d86 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From a1427e66712fc008d76eebdc550345629c487cc0 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:36:32 +0000 Subject: [PATCH 021/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@0.9.7 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 00878849c..555ca0d36 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From cc75746f962f2b06728ae24713023d70bd609e50 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:36:35 +0000 Subject: [PATCH 022/562] chore: bump genkitx-langchain version to genkitx-langchain@0.9.7 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index f123a55e1..ace9f5ef5 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From c2acc96e0a4d61489e799fe2a8dff0f59cb02670 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:36:37 +0000 Subject: [PATCH 023/562] chore: bump genkitx-ollama version to genkitx-ollama@0.9.7 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index e0e716e13..a0e97d56c 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From 30e789dbbab397ffb2abb9d2f02b62c17244fc09 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:36:40 +0000 Subject: [PATCH 024/562] chore: bump genkitx-pinecone version to genkitx-pinecone@0.9.7 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index 5613b0f4f..d22908af7 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From 4f06fd9a459448ffb6e723b0a6d494a311d3ecd4 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:36:42 +0000 Subject: [PATCH 025/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@0.9.7 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 251a66d73..eaef30862 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From 1c0b5febbac8844c79812837ab56973c3da1678c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:36:45 +0000 Subject: [PATCH 026/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@0.9.6 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index 89d6513a2..d0e38e6d3 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "0.9.5", + "version": "0.9.6", "type": "commonjs", "scripts": { "check": "tsc", From 780ca7decd81cb9224a8dd9b8eca1907edec8204 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 5 Dec 2024 16:36:48 +0000 Subject: [PATCH 027/562] chore: bump genkitx-mcp version to genkitx-mcp@0.9.8 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index 382d4c077..22fe3b438 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "0.9.8", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From e496ef5f577062b28557437ad79f0fd0d044fa38 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 5 Dec 2024 11:44:01 -0500 Subject: [PATCH 028/562] chore: gitignore .pnpm-store --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0fb04290b..d53fc1823 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ dist package-lock.json .angular *.tgz +.pnpm-store # version file (autogenerated by CLI) genkit-tools/cli/src/utils/version.ts From 4b85cb1a61a04d9541d2addd54e1fa0b287f7937 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 5 Dec 2024 14:14:57 -0500 Subject: [PATCH 029/562] chore: created release_js_main.yml --- .github/workflows/release_js_main.yml | 43 +++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/release_js_main.yml diff --git a/.github/workflows/release_js_main.yml b/.github/workflows/release_js_main.yml new file mode 100644 index 000000000..578a7eb8f --- /dev/null +++ b/.github/workflows/release_js_main.yml @@ -0,0 +1,43 @@ +# Copyright 2024 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. + +name: Release Genkit JS (latest) + +on: + workflow_dispatch: + +jobs: + build: + name: Run build tasks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v3 + - name: Set up node v21 + uses: actions/setup-node@v4 + with: + node-version: 21.x + cache: 'pnpm' + - name: Install dependencies + run: pnpm install + - name: Run build script + run: pnpm build + - name: Run js tests + run: pnpm test:js + - name: Validate working directory is clean + run: .github/workflows/scripts/ensure-clean-working-tree.sh + - name: release script + run: scripts/release_main.sh + env: + NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} From 19b7b4084ece66ba1d0b12c0ca1b35617bc67c1e Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 5 Dec 2024 14:47:35 -0500 Subject: [PATCH 030/562] refactor(js/core): refactored flow to be an action (#1433) --- .../cli/src/commands/flow-batch-run.ts | 13 +- genkit-tools/cli/src/commands/flow-run.ts | 9 +- genkit-tools/common/src/eval/evaluate.ts | 10 +- genkit-tools/common/src/manager/manager.ts | 23 +++ genkit-tools/common/src/manager/types.ts | 4 + genkit-tools/common/src/server/server.ts | 4 +- genkit-tools/common/src/types/apis.ts | 8 + genkit-tools/common/src/types/flow.ts | 100 ----------- genkit-tools/common/src/types/index.ts | 1 - genkit-tools/common/tests/utils/trace.ts | 1 - genkit-tools/genkit-schema.json | 162 ------------------ genkit-tools/reflectionApi.yaml | 9 + genkit-tools/scripts/schema-exporter.ts | 1 - go/core/action.go | 17 ++ go/genkit/conformance_test.go | 2 +- go/genkit/flow.go | 96 ++--------- go/genkit/servers.go | 45 +++-- go/genkit/testdata/conformance/basic.json | 11 +- go/genkit/testdata/conformance/run-1.json | 11 +- go/internal/version.go | 2 + go/samples/flow-sample1/main.go | 57 ++++++ js/core/src/action.ts | 24 ++- js/core/src/auth.ts | 23 ++- js/core/src/flow.ts | 160 +++++------------ js/core/src/flowTypes.ts | 58 ------- js/core/src/index.ts | 2 +- js/core/src/reflection.ts | 35 +++- js/core/src/tracing/instrumentation.ts | 4 +- js/core/tests/flow_test.ts | 132 +++++++++----- js/genkit/src/index.ts | 8 - .../google-cloud/tests/logs_no_io_test.ts | 2 +- js/plugins/google-cloud/tests/logs_test.ts | 2 +- js/plugins/google-cloud/tests/traces_test.ts | 3 +- js/testapps/flow-sample1/src/index.ts | 28 ++- tests/reflection_api_tests.yaml | 5 - 35 files changed, 408 insertions(+), 664 deletions(-) delete mode 100644 genkit-tools/common/src/types/flow.ts delete mode 100644 js/core/src/flowTypes.ts diff --git a/genkit-tools/cli/src/commands/flow-batch-run.ts b/genkit-tools/cli/src/commands/flow-batch-run.ts index dd2dfdc05..a739cec46 100644 --- a/genkit-tools/cli/src/commands/flow-batch-run.ts +++ b/genkit-tools/cli/src/commands/flow-batch-run.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { FlowInvokeEnvelopeMessage } from '@genkit-ai/tools-common'; import { logger } from '@genkit-ai/tools-common/utils'; import { Command } from 'commander'; import { readFile, writeFile } from 'fs/promises'; @@ -59,13 +58,11 @@ export const flowBatchRun = new Command('flow:batchRun') logger.info(`Running '/flow/${flowName}'...`); let response = await manager.runAction({ key: `/flow/${flowName}`, - input: { - start: { - input: data, - labels: options.label ? { batchRun: options.label } : undefined, - auth: options.auth ? JSON.parse(options.auth) : undefined, - }, - } as FlowInvokeEnvelopeMessage, + input: data, + context: options.auth ? JSON.parse(options.auth) : undefined, + telemetryLabels: options.label + ? { batchRun: options.label } + : undefined, }); logger.info( 'Result:\n' + JSON.stringify(response.result, undefined, ' ') diff --git a/genkit-tools/cli/src/commands/flow-run.ts b/genkit-tools/cli/src/commands/flow-run.ts index b0491a14a..f7954f0a7 100644 --- a/genkit-tools/cli/src/commands/flow-run.ts +++ b/genkit-tools/cli/src/commands/flow-run.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { FlowInvokeEnvelopeMessage } from '@genkit-ai/tools-common'; import { logger } from '@genkit-ai/tools-common/utils'; import { Command } from 'commander'; import { writeFile } from 'fs/promises'; @@ -50,12 +49,8 @@ export const flowRun = new Command('flow:run') await manager.runAction( { key: `/flow/${flowName}`, - input: { - start: { - input: data ? JSON.parse(data) : undefined, - }, - auth: options.auth ? JSON.parse(options.auth) : undefined, - } as FlowInvokeEnvelopeMessage, + input: data ? JSON.parse(data) : undefined, + context: options.auth ? JSON.parse(options.auth) : undefined, }, options.stream ? (chunk) => console.log(JSON.stringify(chunk, undefined, ' ')) diff --git a/genkit-tools/common/src/eval/evaluate.ts b/genkit-tools/common/src/eval/evaluate.ts index 28654afaf..0b4ea4266 100644 --- a/genkit-tools/common/src/eval/evaluate.ts +++ b/genkit-tools/common/src/eval/evaluate.ts @@ -25,7 +25,6 @@ import { EvalKeyAugments, EvalRun, EvalRunKey, - FlowActionInputSchema, GenerateRequest, GenerateRequestSchema, GenerateResponseSchema, @@ -257,15 +256,10 @@ async function runFlowAction(params: { const { manager, actionRef, testCase, auth } = { ...params }; let state: InferenceRunState; try { - const flowInput = FlowActionInputSchema.parse({ - start: { - input: testCase.input, - }, - auth: auth ? JSON.parse(auth) : undefined, - }); const runActionResponse = await manager.runAction({ key: actionRef, - input: flowInput, + input: testCase.input, + context: auth ? JSON.parse(auth) : undefined, }); state = { ...testCase, diff --git a/genkit-tools/common/src/manager/manager.ts b/genkit-tools/common/src/manager/manager.ts index 77269cd39..1f4d15109 100644 --- a/genkit-tools/common/src/manager/manager.ts +++ b/genkit-tools/common/src/manager/manager.ts @@ -42,6 +42,7 @@ import { const STREAM_DELIMITER = '\n'; const HEALTH_CHECK_INTERVAL = 5000; +export const GENKIT_REFLECTION_API_SPEC_VERSION = 1; interface RuntimeManagerOptions { /** URL of the telemetry server. */ @@ -278,6 +279,7 @@ export class RuntimeManager { try { await axios.post(`${runtime.reflectionServerUrl}/api/notify`, { telemetryServerUrl: this.telemetryServerUrl, + reflectionApiSpecVersion: GENKIT_REFLECTION_API_SPEC_VERSION, }); } catch (error) { logger.error(`Failed to notify runtime ${runtime.id}: ${error}`); @@ -326,6 +328,27 @@ export class RuntimeManager { if (isValidRuntimeInfo(runtimeInfo)) { const fileName = path.basename(filePath); if (await checkServerHealth(runtimeInfo.reflectionServerUrl)) { + if ( + runtimeInfo.reflectionApiSpecVersion != + GENKIT_REFLECTION_API_SPEC_VERSION + ) { + if ( + !runtimeInfo.reflectionApiSpecVersion || + runtimeInfo.reflectionApiSpecVersion < + GENKIT_REFLECTION_API_SPEC_VERSION + ) { + logger.warn( + 'Genkit CLI is newer than runtime library. Some feature may not be supported. ' + + 'Consider upgrading your runtime library version (debug info: expected ' + + `${GENKIT_REFLECTION_API_SPEC_VERSION}, got ${runtimeInfo.reflectionApiSpecVersion}).` + ); + } else { + logger.error( + 'Genkit CLI version is outdated. Please update `genkit-cli` to the latest version.' + ); + process.exit(1); + } + } this.filenameToRuntimeMap[fileName] = runtimeInfo; this.idToFileMap[runtimeInfo.id] = fileName; this.eventEmitter.emit(RuntimeEvent.ADD, runtimeInfo); diff --git a/genkit-tools/common/src/manager/types.ts b/genkit-tools/common/src/manager/types.ts index a50bcc51d..3fb9b0325 100644 --- a/genkit-tools/common/src/manager/types.ts +++ b/genkit-tools/common/src/manager/types.ts @@ -38,6 +38,10 @@ export interface RuntimeInfo { timestamp: string; /** Display name for the project, typically basename of the root folder */ projectName?: string; + /** Genkit runtime library version. Ex: nodejs/0.9.5 or go/0.2.0 */ + genkitVersion?: string; + /** Reflection API specification version. Ex: 1 */ + reflectionApiSpecVersion?: number; } export enum RuntimeEvent { diff --git a/genkit-tools/common/src/server/server.ts b/genkit-tools/common/src/server/server.ts index 73237a859..6a2da6b25 100644 --- a/genkit-tools/common/src/server/server.ts +++ b/genkit-tools/common/src/server/server.ts @@ -64,7 +64,7 @@ export function startServer(manager: RuntimeManager, port: number) { }); app.post('/api/streamAction', bodyParser.json(), async (req, res) => { - const { key, input } = req.body; + const { key, input, context } = req.body; res.writeHead(200, { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Content-Type', @@ -72,7 +72,7 @@ export function startServer(manager: RuntimeManager, port: number) { 'Transfer-Encoding': 'chunked', }); - const result = await manager.runAction({ key, input }, (chunk) => { + const result = await manager.runAction({ key, input, context }, (chunk) => { res.write(JSON.stringify(chunk) + '\n'); }); res.write(JSON.stringify(result)); diff --git a/genkit-tools/common/src/types/apis.ts b/genkit-tools/common/src/types/apis.ts index fe0f3128a..bd300cc73 100644 --- a/genkit-tools/common/src/types/apis.ts +++ b/genkit-tools/common/src/types/apis.ts @@ -61,6 +61,14 @@ export const RunActionRequestSchema = z.object({ .any() .optional() .describe('An input with the type that this action expects.'), + context: z + .any() + .optional() + .describe('Additional runtime context data (ex. auth context data).'), + telemetryLabels: z + .record(z.string(), z.string()) + .optional() + .describe('Labels to be applied to telemetry data.'), }); export type RunActionRequest = z.infer; diff --git a/genkit-tools/common/src/types/flow.ts b/genkit-tools/common/src/types/flow.ts deleted file mode 100644 index 2f0a42bd9..000000000 --- a/genkit-tools/common/src/types/flow.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright 2024 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 { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; -import * as z from 'zod'; - -extendZodWithOpenApi(z); - -// NOTE: Keep this file in sync with genkit/flow/src/types.ts! -// Eventually tools will be source of truth for these types (by generating a -// JSON schema) but until then this file must be manually kept in sync - -/** - * The message format used by the flow task queue and control interface. - */ -export const FlowInvokeEnvelopeMessageSchema = z.object({ - // Start new flow. - start: z - .object({ - input: z.unknown().optional(), - labels: z.record(z.string(), z.string()).optional(), - }) - .optional(), - // Schedule new flow. - schedule: z - .object({ - input: z.unknown().optional(), - delay: z.number().optional(), - }) - .optional(), - // Run previously scheduled flow. - runScheduled: z - .object({ - flowId: z.string(), - }) - .optional(), - // Retry failed step (only if step is setup for retry) - retry: z - .object({ - flowId: z.string(), - }) - .optional(), - // Resume an interrupted flow. - resume: z - .object({ - flowId: z.string(), - payload: z.unknown().optional(), - }) - .optional(), - // State check for a given flow ID. No side effects, can be used to check flow state. - state: z - .object({ - flowId: z.string(), - }) - .optional(), -}); -export type FlowInvokeEnvelopeMessage = z.infer< - typeof FlowInvokeEnvelopeMessageSchema ->; - -export const FlowActionInputSchema = FlowInvokeEnvelopeMessageSchema.extend({ - auth: z.unknown().optional(), -}); - -export const FlowStateExecutionSchema = z.object({ - startTime: z - .number() - .optional() - .describe('start time in milliseconds since the epoch'), - endTime: z - .number() - .optional() - .describe('end time in milliseconds since the epoch'), - traceIds: z.array(z.string()), -}); -export type FlowStateExecution = z.infer; - -export const FlowResponseSchema = z.object({ - response: z.unknown().nullable(), -}); -export const FlowErrorSchema = z.object({ - error: z.string().optional(), - stacktrace: z.string().optional(), -}); -export type FlowError = z.infer; - -export const FlowResultSchema = FlowResponseSchema.and(FlowErrorSchema); diff --git a/genkit-tools/common/src/types/index.ts b/genkit-tools/common/src/types/index.ts index 078d050f2..0ddcdc1ee 100644 --- a/genkit-tools/common/src/types/index.ts +++ b/genkit-tools/common/src/types/index.ts @@ -21,7 +21,6 @@ export * from './apis'; export * from './env'; export * from './eval'; export * from './evaluators'; -export * from './flow'; export * from './model'; export * from './prompt'; export * from './retrievers'; diff --git a/genkit-tools/common/tests/utils/trace.ts b/genkit-tools/common/tests/utils/trace.ts index 1b19c39ab..a22c24c0b 100644 --- a/genkit-tools/common/tests/utils/trace.ts +++ b/genkit-tools/common/tests/utils/trace.ts @@ -235,7 +235,6 @@ export class MockTrace { let baseFlowSpan = { ...this.BASE_FLOW_SPAN }; baseFlowSpan.attributes['genkit:input'] = JSON.stringify(flowInput); baseFlowSpan.attributes['genkit:output'] = JSON.stringify(flowOutput); - baseFlowSpan.attributes['genkit:metadata:flow:state'] = baseFlowState; let wrapperActionSpan = { ...this.WRAPPER_ACTION_SPAN }; wrapperActionSpan.attributes['genkit:input'] = JSON.stringify({ diff --git a/genkit-tools/genkit-schema.json b/genkit-tools/genkit-schema.json index e1115db3f..9d629ed14 100644 --- a/genkit-tools/genkit-schema.json +++ b/genkit-tools/genkit-schema.json @@ -932,168 +932,6 @@ "toolResponse" ], "additionalProperties": false - }, - "FlowActionInput": { - "type": "object", - "properties": { - "start": { - "type": "object", - "properties": { - "input": {}, - "labels": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "additionalProperties": false - }, - "schedule": { - "type": "object", - "properties": { - "input": {}, - "delay": { - "type": "number" - } - }, - "additionalProperties": false - }, - "runScheduled": { - "type": "object", - "properties": { - "flowId": { - "type": "string" - } - }, - "required": [ - "flowId" - ], - "additionalProperties": false - }, - "retry": { - "type": "object", - "properties": { - "flowId": { - "type": "string" - } - }, - "required": [ - "flowId" - ], - "additionalProperties": false - }, - "resume": { - "type": "object", - "properties": { - "flowId": { - "type": "string" - }, - "payload": {} - }, - "required": [ - "flowId" - ], - "additionalProperties": false - }, - "state": { - "type": "object", - "properties": { - "flowId": { - "type": "string" - } - }, - "required": [ - "flowId" - ], - "additionalProperties": false - }, - "auth": {} - }, - "additionalProperties": false - }, - "FlowError": { - "type": "object", - "properties": { - "error": { - "type": "string" - }, - "stacktrace": { - "type": "string" - } - }, - "additionalProperties": false - }, - "FlowInvokeEnvelopeMessage": { - "type": "object", - "properties": { - "start": { - "$ref": "#/$defs/FlowActionInput/properties/start" - }, - "schedule": { - "$ref": "#/$defs/FlowActionInput/properties/schedule" - }, - "runScheduled": { - "$ref": "#/$defs/FlowActionInput/properties/runScheduled" - }, - "retry": { - "$ref": "#/$defs/FlowActionInput/properties/retry" - }, - "resume": { - "$ref": "#/$defs/FlowActionInput/properties/resume" - }, - "state": { - "$ref": "#/$defs/FlowActionInput/properties/state" - } - }, - "additionalProperties": false - }, - "FlowResponse": { - "type": "object", - "properties": { - "response": { - "anyOf": [ - {}, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false - }, - "FlowResult": { - "allOf": [ - { - "$ref": "#/$defs/FlowResponse" - }, - { - "$ref": "#/$defs/FlowError" - } - ] - }, - "FlowStateExecution": { - "type": "object", - "properties": { - "startTime": { - "type": "number", - "description": "start time in milliseconds since the epoch" - }, - "endTime": { - "type": "number", - "description": "end time in milliseconds since the epoch" - }, - "traceIds": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "traceIds" - ], - "additionalProperties": false } } } \ No newline at end of file diff --git a/genkit-tools/reflectionApi.yaml b/genkit-tools/reflectionApi.yaml index 0c373830d..683761836 100644 --- a/genkit-tools/reflectionApi.yaml +++ b/genkit-tools/reflectionApi.yaml @@ -332,6 +332,15 @@ paths: input: nullable: true description: An input with the type that this action expects. + context: + nullable: true + description: Additional runtime context data (ex. auth context data). + telemetryLabels: + type: object + nullable: true + additionalProperties: + type: string + description: Labels to be applied to telemetry data. required: - key responses: diff --git a/genkit-tools/scripts/schema-exporter.ts b/genkit-tools/scripts/schema-exporter.ts index 2ab787225..dd7033f6d 100644 --- a/genkit-tools/scripts/schema-exporter.ts +++ b/genkit-tools/scripts/schema-exporter.ts @@ -25,7 +25,6 @@ const EXPORTED_TYPE_MODULES = [ '../common/src/types/trace.ts', '../common/src/types/retrievers.ts', '../common/src/types/model.ts', - '../common/src/types/flow.ts', ]; /** Types that may appear that do not need to be included. */ diff --git a/go/core/action.go b/go/core/action.go index 3cbfc253a..e22c6a399 100644 --- a/go/core/action.go +++ b/go/core/action.go @@ -264,3 +264,20 @@ func LookupActionFor[In, Out, Stream any](typ atype.ActionType, provider, name s } return a.(*Action[In, Out, Stream]) } + +var actionContextKey = base.NewContextKey[int]() + +// WithActionContext returns a new context with action runtime context (side channel data) +// value set. +func WithActionContext(ctx context.Context, actionContext map[string]any) context.Context { + return context.WithValue(ctx, actionContextKey, actionContext) +} + +// ActionContext returns the action runtime context (side channel data) from ctx. +func ActionContext(ctx context.Context) map[string]any { + val := ctx.Value(actionContextKey) + if val == nil { + return nil + } + return val.(map[string]any) +} diff --git a/go/genkit/conformance_test.go b/go/genkit/conformance_test.go index fc10c02b3..c7c1c3c5a 100644 --- a/go/genkit/conformance_test.go +++ b/go/genkit/conformance_test.go @@ -103,7 +103,7 @@ func TestFlowConformance(t *testing.T) { r.TracingState().WriteTelemetryImmediate(tc) _ = defineFlow(r, test.Name, flowFunction(test.Commands)) key := fmt.Sprintf("/flow/%s", test.Name) - resp, err := runAction(context.Background(), r, key, test.Input, nil) + resp, err := runAction(context.Background(), r, key, test.Input, nil, nil) if err != nil { t.Fatal(err) } diff --git a/go/genkit/flow.go b/go/genkit/flow.go index 7f62ef024..6b0e7d838 100644 --- a/go/genkit/flow.go +++ b/go/genkit/flow.go @@ -213,22 +213,26 @@ func defineFlow[In, Out, Stream any](r *registry.Registry, name string, fn core. } f.auth = flowOpts.auth metadata := map[string]any{ - "inputSchema": f.inputSchema, - "outputSchema": f.outputSchema, "requiresAuth": f.auth != nil, } - afunc := func(ctx context.Context, inst *flowInstruction[In], cb func(context.Context, Stream) error) (*flowState[In, Out], error) { + afunc := func(ctx context.Context, input In, cb func(context.Context, Stream) error) (*Out, error) { tracing.SetCustomMetadataAttr(ctx, "flow:wrapperAction", "true") - // Only non-durable flows have an auth policy so can safely assume Start.Input. - if inst.Start != nil { - if f.auth != nil { - ctx = f.auth.NewContext(ctx, inst.Auth) - } - if err := f.checkAuthPolicy(ctx, any(inst.Start.Input)); err != nil { + runtimeContext := core.ActionContext(ctx) + if f.auth != nil { + ctx = f.auth.NewContext(ctx, runtimeContext) + if err := f.checkAuthPolicy(ctx, any(input)); err != nil { return nil, err } } - return f.runInstruction(ctx, inst, streamingCallback[Stream](cb)) + var opts []FlowRunOption + if runtimeContext != nil { + opts = append(opts, WithLocalAuth(runtimeContext)) + } + result, err := f.run(ctx, input, streamingCallback[Stream](cb), opts...) + if err != nil { + return nil, err + } + return &result, err } core.DefineActionInRegistry(r, "", f.name, atype.Flow, metadata, nil, afunc) f.tstate = r.TracingState() @@ -236,54 +240,6 @@ func defineFlow[In, Out, Stream any](r *registry.Registry, name string, fn core. return f } -// TODO: use flowError? - -// A flowInstruction is an instruction to follow with a flow. -// It is the input for the flow's action. -// Exactly one field will be non-nil. -type flowInstruction[In any] struct { - Start *startInstruction[In] `json:"start,omitempty"` - Resume *resumeInstruction `json:"resume,omitempty"` - Schedule *scheduleInstruction[In] `json:"schedule,omitempty"` - RunScheduled *runScheduledInstruction `json:"runScheduled,omitempty"` - State *stateInstruction `json:"state,omitempty"` - Retry *retryInstruction `json:"retry,omitempty"` - Auth map[string]any `json:"auth,omitempty"` -} - -// A startInstruction starts a flow. -type startInstruction[In any] struct { - Input In `json:"input,omitempty"` - Labels map[string]string `json:"labels,omitempty"` -} - -// A resumeInstruction resumes a flow that was started and then interrupted. -type resumeInstruction struct { - FlowID string `json:"flowId,omitempty"` - Payload any `json:"payload,omitempty"` -} - -// A scheduleInstruction schedules a flow to start at a later time. -type scheduleInstruction[In any] struct { - DelaySecs float64 `json:"delay,omitempty"` - Input In `json:"input,omitempty"` -} - -// A runScheduledInstruction starts a scheduled flow. -type runScheduledInstruction struct { - FlowID string `json:"flowId,omitempty"` -} - -// A stateInstruction retrieves the flowState from the flow. -type stateInstruction struct { - FlowID string `json:"flowId,omitempty"` -} - -// TODO: document -type retryInstruction struct { - FlowID string `json:"flowId,omitempty"` -} - // A flowState is a persistent representation of a flow that may be in the middle of running. // It contains all the information needed to resume a flow, including the original input // and a cache of all completed steps. @@ -370,30 +326,6 @@ type FlowResult[Out any] struct { StackTrace string `json:"stacktrace,omitempty"` } -// FlowResult is called FlowResponse in the javascript. - -// runInstruction performs one of several actions on a flow, as determined by msg. -// (Called runEnvelope in the js.) -func (f *Flow[In, Out, Stream]) runInstruction(ctx context.Context, inst *flowInstruction[In], cb streamingCallback[Stream]) (*flowState[In, Out], error) { - switch { - case inst.Start != nil: - // TODO: pass msg.Start.Labels. - return f.start(ctx, inst.Start.Input, cb) - case inst.Resume != nil: - return nil, errors.ErrUnsupported - case inst.Retry != nil: - return nil, errors.ErrUnsupported - case inst.RunScheduled != nil: - return nil, errors.ErrUnsupported - case inst.Schedule != nil: - return nil, errors.ErrUnsupported - case inst.State != nil: - return nil, errors.ErrUnsupported - default: - return nil, errors.New("all known fields of FlowInvokeEnvelopeMessage are nil") - } -} - // The following methods make Flow[I, O, S] implement the flow interface, define in servers.go. // Name returns the name that the flow was defined with. diff --git a/go/genkit/servers.go b/go/genkit/servers.go index e69dab2b8..ef7cd9455 100644 --- a/go/genkit/servers.go +++ b/go/genkit/servers.go @@ -36,8 +36,10 @@ import ( "sync/atomic" "time" + "github.com/firebase/genkit/go/core" "github.com/firebase/genkit/go/core/logger" "github.com/firebase/genkit/go/core/tracing" + "github.com/firebase/genkit/go/internal" "github.com/firebase/genkit/go/internal/action" "github.com/firebase/genkit/go/internal/base" "github.com/firebase/genkit/go/internal/registry" @@ -45,10 +47,12 @@ import ( ) type runtimeFileData struct { - ID string `json:"id"` - PID int `json:"pid"` - ReflectionServerURL string `json:"reflectionServerUrl"` - Timestamp string `json:"timestamp"` + ID string `json:"id"` + PID int `json:"pid"` + ReflectionServerURL string `json:"reflectionServerUrl"` + Timestamp string `json:"timestamp"` + GenkitVersion string `json:"genkitVersion"` + ReflectionApiSpecVersion int `json:"reflectionApiSpecVersion"` } type devServer struct { @@ -94,10 +98,12 @@ func (s *devServer) writeRuntimeFile(url string) error { timestamp := time.Now().UTC().Format(time.RFC3339) s.runtimeFilePath = filepath.Join(runtimesDir, fmt.Sprintf("%d-%s.json", os.Getpid(), timestamp)) data := runtimeFileData{ - ID: runtimeID, - PID: os.Getpid(), - ReflectionServerURL: fmt.Sprintf("http://%s", url), - Timestamp: timestamp, + ID: runtimeID, + PID: os.Getpid(), + ReflectionServerURL: fmt.Sprintf("http://%s", url), + Timestamp: timestamp, + GenkitVersion: "go/" + internal.Version, + ReflectionApiSpecVersion: internal.GENKIT_REFLECTION_API_SPEC_VERSION, } fileContent, err := json.MarshalIndent(data, "", " ") if err != nil { @@ -241,8 +247,9 @@ func newDevServeMux(s *devServer) *http.ServeMux { func (s *devServer) handleRunAction(w http.ResponseWriter, r *http.Request) error { ctx := r.Context() var body struct { - Key string `json:"key"` - Input json.RawMessage `json:"input"` + Key string `json:"key"` + Input json.RawMessage `json:"input"` + Context json.RawMessage `json:"context"` } defer r.Body.Close() if err := json.NewDecoder(r.Body).Decode(&body); err != nil { @@ -271,7 +278,11 @@ func (s *devServer) handleRunAction(w http.ResponseWriter, r *http.Request) erro return nil } } - resp, err := runAction(ctx, s.reg, body.Key, body.Input, callback) + var contextMap map[string]any = nil + if body.Context != nil { + json.Unmarshal(body.Context, &contextMap) + } + resp, err := runAction(ctx, s.reg, body.Key, body.Input, callback, contextMap) if err != nil { return err } @@ -281,7 +292,8 @@ func (s *devServer) handleRunAction(w http.ResponseWriter, r *http.Request) erro // handleNotify configures the telemetry server URL from the request. func (s *devServer) handleNotify(w http.ResponseWriter, r *http.Request) error { var body struct { - TelemetryServerURL string `json:"telemetryServerUrl"` + TelemetryServerURL string `json:"telemetryServerUrl"` + ReflectionApiSpecVersion int `json:"reflectionApiSpecVersion"` } defer r.Body.Close() if err := json.NewDecoder(r.Body).Decode(&body); err != nil { @@ -291,6 +303,9 @@ func (s *devServer) handleNotify(w http.ResponseWriter, r *http.Request) error { s.reg.TracingState().WriteTelemetryImmediate(tracing.NewHTTPTelemetryClient(body.TelemetryServerURL)) slog.Debug("connected to telemetry server", "url", body.TelemetryServerURL) } + if body.ReflectionApiSpecVersion != internal.GENKIT_REFLECTION_API_SPEC_VERSION { + slog.Error("Genkit CLI version is not compatible with runtime library. Please use `genkit-cli` version compatible with runtime library version.") + } w.WriteHeader(http.StatusOK) _, err := w.Write([]byte("OK")) return err @@ -305,11 +320,15 @@ type telemetry struct { TraceID string `json:"traceId"` } -func runAction(ctx context.Context, reg *registry.Registry, key string, input json.RawMessage, cb streamingCallback[json.RawMessage]) (*runActionResponse, error) { +func runAction(ctx context.Context, reg *registry.Registry, key string, input json.RawMessage, cb streamingCallback[json.RawMessage], runtimeContext map[string]any) (*runActionResponse, error) { action := reg.LookupAction(key) if action == nil { return nil, &base.HTTPError{Code: http.StatusNotFound, Err: fmt.Errorf("no action with key %q", key)} } + if runtimeContext != nil { + ctx = core.WithActionContext(ctx, runtimeContext) + } + var traceID string output, err := tracing.RunInNewSpan(ctx, reg.TracingState(), "dev-run-action-wrapper", "", true, input, func(ctx context.Context, input json.RawMessage) (json.RawMessage, error) { tracing.SetCustomMetadataAttr(ctx, "genkit-dev-internal", "true") diff --git a/go/genkit/testdata/conformance/basic.json b/go/genkit/testdata/conformance/basic.json index 67a39b2c1..3428e8fce 100644 --- a/go/genkit/testdata/conformance/basic.json +++ b/go/genkit/testdata/conformance/basic.json @@ -5,13 +5,8 @@ {"run": {"name": "call-llm", "command": {"append": "y"}}}, {"run": {"name": "call-llm", "command": {"append": "z"}}} ], - "input": {"start": {"input": "x"}}, - "result": { - "operation": { - "done": true, - "result": {"response": "xyz"} - } - }, + "input": "x", + "result": "xyz", "trace": { "displayName": "dev-run-action-wrapper", "spans": { @@ -110,7 +105,7 @@ "genkit:type": "action", "genkit:name": "basic", "genkit:path": "/dev-run-action-wrapper/basic", - "genkit:input": "{\"start\":{\"input\":\"x\"}}", + "genkit:input": "\"x\"", "genkit:metadata:flow:wrapperAction": "true", "genkit:output": "$ANYTHING", "genkit:state": "success" diff --git a/go/genkit/testdata/conformance/run-1.json b/go/genkit/testdata/conformance/run-1.json index 5e904bd0b..a2c779574 100644 --- a/go/genkit/testdata/conformance/run-1.json +++ b/go/genkit/testdata/conformance/run-1.json @@ -1,13 +1,8 @@ { "name": "run", "commands": [{"run" :{"name": "r", "command": {"append": "x"}}}], - "input": {"start": {"input": ""}}, - "result": { - "operation": { - "done": true, - "result": {"response": "x"} - } - }, + "input": "", + "result": "x", "trace": { "displayName": "dev-run-action-wrapper", "spans": { @@ -73,7 +68,7 @@ "genkit:type": "action", "genkit:name": "run", "genkit:path": "/dev-run-action-wrapper/run", - "genkit:input": "{\"start\":{}}", + "genkit:input": "\"\"", "genkit:metadata:flow:wrapperAction": "true", "genkit:output": "$ANYTHING", "_comment": "The output above is a JSON object with various random IDs", diff --git a/go/internal/version.go b/go/internal/version.go index 6b9f6247f..d1cc79ca5 100644 --- a/go/internal/version.go +++ b/go/internal/version.go @@ -17,3 +17,5 @@ package internal // Version is the current tagged release of this module. // That is, it should match the value of the latest `go/v*` git tag. const Version = "0.2.0" + +const GENKIT_REFLECTION_API_SPEC_VERSION = 1 diff --git a/go/samples/flow-sample1/main.go b/go/samples/flow-sample1/main.go index 2c33237a6..68f119035 100644 --- a/go/samples/flow-sample1/main.go +++ b/go/samples/flow-sample1/main.go @@ -34,8 +34,10 @@ package main import ( "context" + "encoding/json" "errors" "fmt" + "log" "strconv" @@ -51,6 +53,17 @@ func main() { return genkit.Run(ctx, "call-llm", func() (string, error) { return "foo: " + foo, nil }) }) + auth := &testAuth{} + + genkit.DefineFlow("withContext", func(ctx context.Context, subject string) (string, error) { + authJson, err := json.Marshal(auth.FromContext(ctx)) + if err != nil { + return "", err + } + + return "subject=" + subject + ",auth=" + string(authJson), nil + }, genkit.WithFlowAuth(auth)) + genkit.DefineFlow("parent", func(ctx context.Context, _ struct{}) (string, error) { return basic.Run(ctx, "foo") }) @@ -92,3 +105,47 @@ func main() { log.Fatal(err) } } + +type testAuth struct { + genkit.FlowAuth +} + +const authKey = "testAuth" + +// ProvideAuthContext provides auth context from an auth header and sets it on the context. +func (f *testAuth) ProvideAuthContext(ctx context.Context, authHeader string) (context.Context, error) { + var context genkit.AuthContext + context = map[string]any{ + "username": authHeader, + } + return f.NewContext(ctx, context), nil +} + +// NewContext sets the auth context on the given context. +func (f *testAuth) NewContext(ctx context.Context, authContext genkit.AuthContext) context.Context { + return context.WithValue(ctx, authKey, authContext) +} + +// FromContext retrieves the auth context from the given context. +func (*testAuth) FromContext(ctx context.Context) genkit.AuthContext { + if ctx == nil { + return nil + } + val := ctx.Value(authKey) + if val == nil { + return nil + } + return val.(genkit.AuthContext) +} + +// CheckAuthPolicy checks auth context against policy. +func (f *testAuth) CheckAuthPolicy(ctx context.Context, input any) error { + authContext := f.FromContext(ctx) + if authContext == nil { + return errors.New("auth is required") + } + if authContext["username"] != "authorized" { + return errors.New("unauthorized") + } + return nil +} diff --git a/js/core/src/action.ts b/js/core/src/action.ts index 92595be88..fe18083a5 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -71,6 +71,11 @@ export interface ActionRunOptions { * Additional runtime context data (ex. auth context data). */ context?: any; + + /** + * Additional span attributes to apply to OT spans. + */ + telemetryLabels?: Record; } /** @@ -127,7 +132,8 @@ type ActionParams< outputJsonSchema?: JSONSchema7; metadata?: Record; use?: Middleware, z.infer, z.infer>[]; - streamingSchema?: S; + streamSchema?: S; + actionType: ActionType; }; export type SimpleMiddleware = ( @@ -248,9 +254,18 @@ export function action< name: actionName, labels: { [SPAN_TYPE_ATTR]: 'action', + 'genkit:metadata:subtype': config.actionType, + ...options?.telemetryLabels, }, }, async (metadata, span) => { + setCustomMetadataAttributes({ subtype: config.actionType }); + if (options?.context) { + setCustomMetadataAttributes({ + context: JSON.stringify(options.context), + }); + } + traceId = span.spanContext().traceId; spanId = span.spanContext().spanId; metadata.name = actionName; @@ -317,14 +332,12 @@ export function defineAction< S extends z.ZodTypeAny = z.ZodTypeAny, >( registry: Registry, - config: ActionParams & { - actionType: ActionType; - }, + config: ActionParams, fn: ( input: z.infer, options: ActionFnArg> ) => Promise> -): Action { +): Action { if (isInRuntimeContext()) { throw new Error( 'Cannot define new actions at runtime.\n' + @@ -337,7 +350,6 @@ export function defineAction< validateActionId(config.name.actionId); } const act = action(config, async (i: I, options): Promise> => { - setCustomMetadataAttributes({ subtype: config.actionType }); await registry.initializeAllPlugins(); return await runInActionRuntimeContext(() => fn(i, options)); }); diff --git a/js/core/src/auth.ts b/js/core/src/auth.ts index 7e05df0c5..753be5153 100644 --- a/js/core/src/auth.ts +++ b/js/core/src/auth.ts @@ -17,19 +17,30 @@ import { AsyncLocalStorage } from 'node:async_hooks'; import { runInActionRuntimeContext } from './action.js'; -const authAsyncLocalStorage = new AsyncLocalStorage(); +const contextAsyncLocalStorage = new AsyncLocalStorage(); /** - * Execute the provided function in the auth context. Call {@link getFlowAuth()} anywhere - * within the async call stack to retrieve the auth. + * Execute the provided function in the runtime context. Call {@link getFlowContext()} anywhere + * within the async call stack to retrieve the context. */ -export function runWithAuthContext(auth: any, fn: () => R) { - return authAsyncLocalStorage.run(auth, () => runInActionRuntimeContext(fn)); +export function runWithContext(context: any, fn: () => R) { + return contextAsyncLocalStorage.run(context, () => + runInActionRuntimeContext(fn) + ); } /** * Gets the auth object from the current context. + * + * @deprecated use {@link getFlowContext} */ export function getFlowAuth(): any { - return authAsyncLocalStorage.getStore(); + return contextAsyncLocalStorage.getStore(); +} + +/** + * Gets the runtime context of the current flow. + */ +export function getFlowContext(): any { + return contextAsyncLocalStorage.getStore(); } diff --git a/js/core/src/flow.ts b/js/core/src/flow.ts index 60d193146..2dd53d802 100644 --- a/js/core/src/flow.ts +++ b/js/core/src/flow.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { SpanStatusCode } from '@opentelemetry/api'; import * as bodyParser from 'body-parser'; import cors, { CorsOptions } from 'cors'; import express from 'express'; @@ -22,24 +21,15 @@ import { Server } from 'http'; import { z } from 'zod'; import { Action, + ActionResult, defineAction, - getStreamingCallback, StreamingCallback, } from './action.js'; -import { runWithAuthContext } from './auth.js'; +import { runWithContext } from './auth.js'; import { getErrorMessage, getErrorStack } from './error.js'; -import { FlowActionInputSchema } from './flowTypes.js'; import { logger } from './logging.js'; import { Registry } from './registry.js'; -import { toJsonSchema } from './schema.js'; -import { - newTrace, - runInNewSpan, - setCustomMetadataAttribute, - setCustomMetadataAttributes, - SPAN_TYPE_ATTR, -} from './tracing.js'; -import { flowMetadataPrefix } from './utils.js'; +import { runInNewSpan, SPAN_TYPE_ATTR } from './tracing.js'; const streamDelimiter = '\n\n'; @@ -109,7 +99,7 @@ export interface CallableFlow< stream(input?: z.infer, opts?: FlowCallOptions): StreamingResponse; - flow: Flow; + flow: Flow; } /** @@ -152,18 +142,6 @@ export type FlowFn< streamingCallback: StreamingCallback> ) => Promise> | z.infer; -/** - * Represents the result of a flow execution. - */ -interface FlowResult { - /** The result of the flow execution. */ - result: O; - /** The trace ID associated with the flow execution. */ - traceId: string; - /** The root span ID of the associated trace. */ - spanId: string; -} - export class Flow< I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, @@ -175,12 +153,12 @@ export class Flow< readonly streamSchema?: S; readonly authPolicy?: FlowAuthPolicy; readonly middleware?: express.RequestHandler[]; - readonly flowFn: FlowFn; + readonly action: Action; constructor( private registry: Registry, config: FlowConfig | StreamingFlowConfig, - flowFn: FlowFn + action: Action ) { this.name = config.name; this.inputSchema = config.inputSchema; @@ -189,7 +167,7 @@ export class Flow< 'streamSchema' in config ? config.streamSchema : undefined; this.authPolicy = config.authPolicy; this.middleware = config.middleware; - this.flowFn = flowFn; + this.action = action; } /** @@ -200,61 +178,15 @@ export class Flow< opts: { streamingCallback?: StreamingCallback>; labels?: Record; - auth?: unknown; + context?: unknown; } - ): Promise>> { + ): Promise>> { await this.registry.initializeAllPlugins(); - return await runWithAuthContext(opts.auth, () => - newTrace( - { - name: this.name, - labels: { - [SPAN_TYPE_ATTR]: 'flow', - }, - }, - async (metadata, rootSpan) => { - if (opts.labels) { - const labels = opts.labels; - Object.keys(opts.labels).forEach((label) => { - setCustomMetadataAttribute( - flowMetadataPrefix(`label:${label}`), - labels[label] - ); - }); - } - - setCustomMetadataAttributes({ - [flowMetadataPrefix('name')]: this.name, - }); - try { - metadata.input = input; - const output = await this.flowFn( - input, - opts.streamingCallback ?? (() => {}) - ); - metadata.output = JSON.stringify(output); - setCustomMetadataAttribute(flowMetadataPrefix('state'), 'done'); - return { - result: output, - traceId: rootSpan.spanContext().traceId, - spanId: rootSpan.spanContext().spanId, - }; - } catch (e) { - metadata.state = 'error'; - rootSpan.setStatus({ - code: SpanStatusCode.ERROR, - message: getErrorMessage(e), - }); - if (e instanceof Error) { - rootSpan.recordException(e); - } - - setCustomMetadataAttribute(flowMetadataPrefix('state'), 'error'); - throw e; - } - } - ) - ); + return await this.action.run(input, { + context: opts.context, + telemetryLabels: opts.labels, + onChunk: opts.streamingCallback ?? (() => {}), + }); } /** @@ -271,7 +203,7 @@ export class Flow< } const result = await this.invoke(input, { - auth: opts?.context || opts?.withLocalAuthContext, + context: opts?.context || opts?.withLocalAuthContext, }); return result.result; } @@ -306,7 +238,7 @@ export class Flow< }) as S extends z.ZodVoid ? undefined : StreamingCallback>, - auth: opts?.context || opts?.withLocalAuthContext, + context: opts?.context || opts?.withLocalAuthContext, } ).then((s) => s.result) ) @@ -366,7 +298,7 @@ export class Flow< 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter ); }, - auth, + context: auth, }); response.write( 'data: ' + JSON.stringify({ result: result.result }) + streamDelimiter @@ -389,9 +321,9 @@ export class Flow< } } else { try { - const result = await this.invoke(input, { auth }); - response.setHeader('x-genkit-trace-id', result.traceId); - response.setHeader('x-genkit-span-id', result.spanId); + const result = await this.invoke(input, { context: auth }); + response.setHeader('x-genkit-trace-id', result.telemetry.traceId); + response.setHeader('x-genkit-span-id', result.telemetry.spanId); // Responses for non-streaming flows are passed back with the flow result stored in a field called "result." response .status(200) @@ -538,18 +470,15 @@ export function defineFlow< S extends z.ZodTypeAny = z.ZodTypeAny, >( registry: Registry, - config: StreamingFlowConfig | string, + config: StreamingFlowConfig | string, fn: FlowFn ): CallableFlow { - const resolvedConfig: FlowConfig = + const resolvedConfig: StreamingFlowConfig = typeof config === 'string' ? { name: config } : config; - const flow = new Flow(registry, resolvedConfig, fn); - registerFlowAction(registry, flow); - const callableFlow = async ( - input: z.infer, - opts: FlowCallOptions - ): Promise> => { + const flowAction = defineFlowAction(registry, resolvedConfig, fn); + const flow = new Flow(registry, resolvedConfig, flowAction); + const callableFlow = async (input, opts) => { return flow.run(input, opts); }; (callableFlow as CallableFlow).flow = flow; @@ -574,8 +503,8 @@ export function defineStreamingFlow< config: StreamingFlowConfig, fn: FlowFn ): StreamableFlow { - const flow = new Flow(registry, config, fn); - registerFlowAction(registry, flow); + const flowAction = defineFlowAction(registry, config, fn); + const flow = new Flow(registry, config, flowAction); const streamableFlow: StreamableFlow = (input, opts) => { return flow.stream(input, opts); }; @@ -586,41 +515,30 @@ export function defineStreamingFlow< /** * Registers a flow as an action in the registry. */ -function registerFlowAction< +function defineFlowAction< I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny, >( registry: Registry, - flow: Flow -): Action { + config: StreamingFlowConfig, + fn: FlowFn +): Action { return defineAction( registry, { actionType: 'flow', - name: flow.name, - inputSchema: FlowActionInputSchema, - outputSchema: flow.outputSchema, + name: config.name, + inputSchema: config.inputSchema, + outputSchema: config.outputSchema, + streamSchema: config.streamSchema, metadata: { - inputSchema: toJsonSchema({ schema: flow.inputSchema }), - outputSchema: toJsonSchema({ schema: flow.outputSchema }), - requiresAuth: !!flow.authPolicy, + requiresAuth: !!config.authPolicy, }, }, - async (envelope) => { - await flow.authPolicy?.( - envelope.auth, - envelope.start?.input as I | undefined - ); - setCustomMetadataAttribute(flowMetadataPrefix('wrapperAction'), 'true'); - const response = await flow.invoke(envelope.start?.input, { - streamingCallback: getStreamingCallback() as S extends z.ZodVoid - ? undefined - : StreamingCallback>, - auth: envelope.auth, - labels: envelope.start?.labels, - }); - return response.result; + async (input, { sendChunk, context }) => { + await config.authPolicy?.(context, input); + return await runWithContext(context, () => fn(input, sendChunk)); } ); } diff --git a/js/core/src/flowTypes.ts b/js/core/src/flowTypes.ts deleted file mode 100644 index bb56bf530..000000000 --- a/js/core/src/flowTypes.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2024 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 { z } from 'zod'; - -// NOTE: Keep this file in sync with genkit-tools/src/types/flow.ts! -// Eventually tools will be source of truth for these types (by generating a -// JSON schema) but until then this file must be manually kept in sync - -export const FlowResponseSchema = z.object({ - response: z.unknown().nullable(), -}); - -export const FlowErrorSchema = z.object({ - error: z.string().optional(), - stacktrace: z.string().optional(), -}); - -export type FlowError = z.infer; - -export const FlowResultSchema = FlowResponseSchema.and(FlowErrorSchema); - -/** - * Used for flow control. - */ -export const FlowInvokeEnvelopeMessageSchema = z.object({ - // Start new flow. - start: z.object({ - input: z.unknown().optional(), - labels: z.record(z.string(), z.string()).optional(), - }), -}); - -export type FlowInvokeEnvelopeMessage = z.infer< - typeof FlowInvokeEnvelopeMessageSchema ->; - -/** - * Used by the flow action. - */ -export const FlowActionInputSchema = FlowInvokeEnvelopeMessageSchema.extend({ - auth: z.unknown().optional(), -}); - -export type FlowActionInput = z.infer; diff --git a/js/core/src/index.ts b/js/core/src/index.ts index 3bd1c1ad3..f8d4ce85a 100644 --- a/js/core/src/index.ts +++ b/js/core/src/index.ts @@ -18,6 +18,7 @@ import { version } from './__codegen/version.js'; export const GENKIT_VERSION = version; export const GENKIT_CLIENT_HEADER = `genkit-node/${GENKIT_VERSION} gl-node/${process.versions.node}`; +export const GENKIT_REFLECTION_API_SPEC_VERSION = 1; export { z } from 'zod'; export * from './action.js'; @@ -38,7 +39,6 @@ export { type StreamingFlowConfig, type __RequestWithAuth, } from './flow.js'; -export * from './flowTypes.js'; export * from './plugin.js'; export * from './reflection.js'; export { defineJsonSchema, defineSchema, type JSONSchema } from './schema.js'; diff --git a/js/core/src/reflection.ts b/js/core/src/reflection.ts index 4ebf30a69..dae0054e1 100644 --- a/js/core/src/reflection.ts +++ b/js/core/src/reflection.ts @@ -21,7 +21,7 @@ import { Server } from 'http'; import path from 'path'; import z from 'zod'; import { Status, StatusCodes, runWithStreamingCallback } from './action.js'; -import { GENKIT_VERSION } from './index.js'; +import { GENKIT_REFLECTION_API_SPEC_VERSION, GENKIT_VERSION } from './index.js'; import { logger } from './logging.js'; import { Registry } from './registry.js'; import { toJsonSchema } from './schema.js'; @@ -153,7 +153,7 @@ export class ReflectionServer { }); server.post('/api/runAction', async (request, response, next) => { - const { key, input } = request.body; + const { key, input, context, telemetryLabels } = request.body; const { stream } = request.query; logger.debug(`Running action \`${key}\` with stream=${stream}...`); let traceId; @@ -164,11 +164,12 @@ export class ReflectionServer { return; } if (stream === 'true') { + const callback = (chunk) => { + response.write(JSON.stringify(chunk) + '\n'); + }; const result = await runWithStreamingCallback( - (chunk) => { - response.write(JSON.stringify(chunk) + '\n'); - }, - async () => await action.run(input) + callback, + async () => await action.run(input, { context, onChunk: callback }) ); await flushTracing(); response.write( @@ -181,7 +182,7 @@ export class ReflectionServer { ); response.end(); } else { - const result = await action.run(input); + const result = await action.run(input, { context, telemetryLabels }); await flushTracing(); response.send({ result: result.result, @@ -201,11 +202,27 @@ export class ReflectionServer { }); server.post('/api/notify', async (request, response) => { - const { telemetryServerUrl } = request.body; + const { telemetryServerUrl, reflectionApiSpecVersion } = request.body; if (typeof telemetryServerUrl === 'string') { setTelemetryServerUrl(telemetryServerUrl); logger.debug(`Connected to telemetry server on ${telemetryServerUrl}`); } + if (reflectionApiSpecVersion !== GENKIT_REFLECTION_API_SPEC_VERSION) { + if ( + !reflectionApiSpecVersion || + reflectionApiSpecVersion < GENKIT_REFLECTION_API_SPEC_VERSION + ) { + logger.warn( + 'WARNING: Genkit CLI version may be outdated. Please update `genkit-cli` to the latest version.' + ); + } else { + logger.warn( + 'Genkit CLI is newer than runtime library. Some feature may not be supported. ' + + 'Consider upgrading your runtime library version (debug info: expected ' + + `${GENKIT_REFLECTION_API_SPEC_VERSION}, got ${reflectionApiSpecVersion}).` + ); + } + } response.status(200).send('OK'); }); @@ -286,6 +303,8 @@ export class ReflectionServer { pid: process.pid, reflectionServerUrl: `http://localhost:${this.port}`, timestamp, + genkitVersion: `nodejs/${GENKIT_VERSION}`, + reflectionApiSpecVersion: GENKIT_REFLECTION_API_SPEC_VERSION, }, null, 2 diff --git a/js/core/src/tracing/instrumentation.ts b/js/core/src/tracing/instrumentation.ts index 2203ef1f2..0dd0173cb 100644 --- a/js/core/src/tracing/instrumentation.ts +++ b/js/core/src/tracing/instrumentation.ts @@ -230,7 +230,9 @@ function buildPath( labels?: Record ) { const stepType = - labels && labels['genkit:type'] ? `,t:${labels['genkit:type']}` : ''; + labels && labels['genkit:type'] + ? `,t:${labels['genkit:metadata:subtype'] === 'flow' ? 'flow' : labels['genkit:type']}` + : ''; return parentPath + `/{${name}${stepType}}`; } diff --git a/js/core/tests/flow_test.ts b/js/core/tests/flow_test.ts index 5a3df6e7f..cecbab523 100644 --- a/js/core/tests/flow_test.ts +++ b/js/core/tests/flow_test.ts @@ -17,6 +17,7 @@ import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; +import { getFlowContext } from '../src/auth.js'; import { defineFlow, defineStreamingFlow, run } from '../src/flow.js'; import { defineAction, getFlowAuth, z } from '../src/index.js'; import { Registry } from '../src/registry.js'; @@ -42,40 +43,6 @@ function createTestFlow(registry: Registry) { ); } -function createTestAuthFlow(registry: Registry) { - return defineFlow( - registry, - { - name: 'testFlow', - inputSchema: z.string(), - outputSchema: z.string(), - }, - async (input) => { - return `bar ${input} ${JSON.stringify(getFlowAuth())}`; - } - ); -} - -function createTestAuthStreamingFlow(registry: Registry) { - return defineStreamingFlow( - registry, - { - name: 'testFlow', - inputSchema: z.number(), - outputSchema: z.string(), - streamSchema: z.object({ count: z.number() }), - }, - async (input, streamingCallback) => { - if (streamingCallback) { - for (let i = 0; i < input; i++) { - streamingCallback({ count: i }); - } - } - return `bar ${input} ${!!streamingCallback} ${JSON.stringify(getFlowAuth())}`; - } - ); -} - function createTestStreamingFlow(registry: Registry) { return defineStreamingFlow( registry, @@ -207,7 +174,17 @@ describe('flow', () => { describe('getFlowAuth', () => { it('should run the flow', async () => { - const testFlow = createTestAuthFlow(registry); + const testFlow = defineFlow( + registry, + { + name: 'testFlow', + inputSchema: z.string(), + outputSchema: z.string(), + }, + async (input) => { + return `bar ${input} ${JSON.stringify(getFlowAuth())}`; + } + ); const response = await testFlow('foo', { withLocalAuthContext: { user: 'test-user' }, @@ -217,7 +194,23 @@ describe('flow', () => { }); it('should streams the flow', async () => { - const testFlow = createTestAuthStreamingFlow(registry); + const testFlow = defineStreamingFlow( + registry, + { + name: 'testFlow', + inputSchema: z.number(), + outputSchema: z.string(), + streamSchema: z.object({ count: z.number() }), + }, + async (input, streamingCallback) => { + if (streamingCallback) { + for (let i = 0; i < input; i++) { + streamingCallback({ count: i }); + } + } + return `bar ${input} ${!!streamingCallback} ${JSON.stringify(getFlowAuth())}`; + } + ); const response = testFlow(3, { withLocalAuthContext: { user: 'test-user' }, @@ -233,6 +226,60 @@ describe('flow', () => { }); }); + describe('getFlowContext', () => { + it('should run the flow', async () => { + const testFlow = defineFlow( + registry, + { + name: 'testFlow', + inputSchema: z.string(), + outputSchema: z.string(), + }, + async (input) => { + return `bar ${input} ${JSON.stringify(getFlowContext())}`; + } + ); + + const response = await testFlow('foo', { + context: { user: 'test-user' }, + }); + + assert.equal(response, 'bar foo {"user":"test-user"}'); + }); + + it('should streams the flow', async () => { + const testFlow = defineStreamingFlow( + registry, + { + name: 'testFlow', + inputSchema: z.number(), + outputSchema: z.string(), + streamSchema: z.object({ count: z.number() }), + }, + async (input, streamingCallback) => { + if (streamingCallback) { + for (let i = 0; i < input; i++) { + streamingCallback({ count: i }); + } + } + return `bar ${input} ${!!streamingCallback} ${JSON.stringify(getFlowContext())}`; + } + ); + + const response = testFlow(3, { + context: { user: 'test-user' }, + }); + + const gotChunks: any[] = []; + for await (const chunk of response.stream) { + gotChunks.push(chunk); + } + + assert.equal(await response.output, 'bar 3 true {"user":"test-user"}'); + assert.deepEqual(gotChunks, [{ count: 0 }, { count: 1 }, { count: 2 }]); + }); + }); + describe('telemetry', async () => { beforeEach(() => { spanExporter.exportedSpans = []; @@ -249,13 +296,12 @@ describe('flow', () => { assert.deepStrictEqual(spanExporter.exportedSpans[0].attributes, { 'genkit:input': '"foo"', 'genkit:isRoot': true, - 'genkit:metadata:flow:name': 'testFlow', - 'genkit:metadata:flow:state': 'done', + 'genkit:metadata:subtype': 'flow', 'genkit:name': 'testFlow', 'genkit:output': '"bar foo"', 'genkit:path': '/{testFlow,t:flow}', 'genkit:state': 'success', - 'genkit:type': 'flow', + 'genkit:type': 'action', }); }); @@ -285,7 +331,7 @@ describe('flow', () => { }); } ); - const result = await testFlow('foo'); + const result = await testFlow('foo', { context: { user: 'pavel' } }); assert.equal(result, 'foo bar'); assert.strictEqual(spanExporter.exportedSpans.length, 3); @@ -317,13 +363,13 @@ describe('flow', () => { assert.deepStrictEqual(spanExporter.exportedSpans[2].attributes, { 'genkit:input': '"foo"', 'genkit:isRoot': true, - 'genkit:metadata:flow:name': 'testFlow', - 'genkit:metadata:flow:state': 'done', + 'genkit:metadata:subtype': 'flow', + 'genkit:metadata:context': '{"user":"pavel"}', 'genkit:name': 'testFlow', 'genkit:output': '"foo bar"', 'genkit:path': '/{testFlow,t:flow}', 'genkit:state': 'success', - 'genkit:type': 'flow', + 'genkit:type': 'action', }); }); }); diff --git a/js/genkit/src/index.ts b/js/genkit/src/index.ts index bc7dd3e0d..33b7eb36a 100644 --- a/js/genkit/src/index.ts +++ b/js/genkit/src/index.ts @@ -101,9 +101,6 @@ export { } from '@genkit-ai/ai'; export { type SessionData, type SessionStore } from '@genkit-ai/ai/session'; export { - FlowActionInputSchema, - FlowErrorSchema, - FlowInvokeEnvelopeMessageSchema, FlowServer, GENKIT_CLIENT_HEADER, GENKIT_VERSION, @@ -129,14 +126,9 @@ export { type ActionMetadata, type CallableFlow, type Flow, - type FlowActionInput, type FlowAuthPolicy, type FlowConfig, - type FlowError, type FlowFn, - type FlowInvokeEnvelopeMessage, - type FlowResponseSchema, - type FlowResultSchema, type FlowServerOptions, type JSONSchema, type JSONSchema7, diff --git a/js/plugins/google-cloud/tests/logs_no_io_test.ts b/js/plugins/google-cloud/tests/logs_no_io_test.ts index a7111a610..8025580c1 100644 --- a/js/plugins/google-cloud/tests/logs_no_io_test.ts +++ b/js/plugins/google-cloud/tests/logs_no_io_test.ts @@ -219,7 +219,7 @@ function createFlowWithInput( { name, inputSchema: z.string(), - outputSchema: z.string(), + outputSchema: z.any(), }, fn ); diff --git a/js/plugins/google-cloud/tests/logs_test.ts b/js/plugins/google-cloud/tests/logs_test.ts index ae4e45d04..01ffa9c0b 100644 --- a/js/plugins/google-cloud/tests/logs_test.ts +++ b/js/plugins/google-cloud/tests/logs_test.ts @@ -272,7 +272,7 @@ function createFlowWithInput( { name, inputSchema: z.string(), - outputSchema: z.string(), + outputSchema: z.any(), }, fn ); diff --git a/js/plugins/google-cloud/tests/traces_test.ts b/js/plugins/google-cloud/tests/traces_test.ts index ffa401c16..fefaa4c37 100644 --- a/js/plugins/google-cloud/tests/traces_test.ts +++ b/js/plugins/google-cloud/tests/traces_test.ts @@ -91,7 +91,8 @@ describe('GoogleCloudTracing', () => { const spans = await getExportedSpans(); // Check some common attributes assert.equal(spans[0].attributes['genkit/name'], 'testFlow'); - assert.equal(spans[0].attributes['genkit/type'], 'flow'); + assert.equal(spans[0].attributes['genkit/type'], 'action'); + assert.equal(spans[0].attributes['genkit/metadata/subtype'], 'flow'); // Ensure we have no attributes with ':' because these are awkward to use in // Cloud Trace. const spanAttrKeys = Object.entries(spans[0].attributes).map(([k, v]) => k); diff --git a/js/testapps/flow-sample1/src/index.ts b/js/testapps/flow-sample1/src/index.ts index 045bb224b..fcaeb84a4 100644 --- a/js/testapps/flow-sample1/src/index.ts +++ b/js/testapps/flow-sample1/src/index.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { genkit, run, z } from 'genkit'; +import { genkit, getFlowAuth, run, z } from 'genkit'; const ai = genkit({}); @@ -22,7 +22,7 @@ const ai = genkit({}); * To run this flow; * genkit flow:run basic "\"hello\"" */ -export const basic = ai.defineFlow({ name: 'basic' }, async (subject) => { +export const basic = ai.defineFlow('basic', async (subject) => { const foo = await run('call-llm', async () => { return `subject: ${subject}`; }); @@ -39,6 +39,30 @@ export const parent = ai.defineFlow( } ); +export const withInputSchema = ai.defineFlow( + { name: 'withInputSchema', inputSchema: z.object({ subject: z.string() }) }, + async (input) => { + const foo = await run('call-llm', async () => { + return `subject: ${input.subject}`; + }); + + return await run('call-llm1', async () => { + return `foo: ${foo}`; + }); + } +); + +export const withContext = ai.defineFlow( + { + name: 'withContext', + inputSchema: z.object({ subject: z.string() }), + authPolicy: () => {}, + }, + async (input) => { + return `subject: ${input.subject}, context: ${JSON.stringify(getFlowAuth())}`; + } +); + // genkit flow:run streamy 5 -s export const streamy = ai.defineStreamingFlow( { diff --git a/tests/reflection_api_tests.yaml b/tests/reflection_api_tests.yaml index 0f68a54b1..060ce3302 100644 --- a/tests/reflection_api_tests.yaml +++ b/tests/reflection_api_tests.yaml @@ -876,8 +876,3 @@ tests: /flow/testFlow: key: /flow/testFlow name: testFlow - metadata: - inputSchema: - type: string - outputSchema: - type: string From 0f01c29daeb75d1815a9e990ce0d9c664bec6e15 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 5 Dec 2024 16:28:05 -0500 Subject: [PATCH 031/562] chore: update release_js_main.yml --- .github/workflows/release_js_main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release_js_main.yml b/.github/workflows/release_js_main.yml index 578a7eb8f..a7ab76344 100644 --- a/.github/workflows/release_js_main.yml +++ b/.github/workflows/release_js_main.yml @@ -29,6 +29,7 @@ jobs: with: node-version: 21.x cache: 'pnpm' + registry-url: 'https://wombat-dressing-room.appspot.com/' - name: Install dependencies run: pnpm install - name: Run build script From 0cd8a06241805b530007dbc0626ce628655fc698 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 5 Dec 2024 16:30:31 -0500 Subject: [PATCH 032/562] chore: update release_js_main.yml --- .github/workflows/release_js_main.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release_js_main.yml b/.github/workflows/release_js_main.yml index a7ab76344..f7106c799 100644 --- a/.github/workflows/release_js_main.yml +++ b/.github/workflows/release_js_main.yml @@ -29,7 +29,6 @@ jobs: with: node-version: 21.x cache: 'pnpm' - registry-url: 'https://wombat-dressing-room.appspot.com/' - name: Install dependencies run: pnpm install - name: Run build script @@ -38,6 +37,12 @@ jobs: run: pnpm test:js - name: Validate working directory is clean run: .github/workflows/scripts/ensure-clean-working-tree.sh + - name: Set up node v21 + uses: actions/setup-node@v4 + with: + node-version: 21.x + cache: 'pnpm' + registry-url: 'https://wombat-dressing-room.appspot.com/' - name: release script run: scripts/release_main.sh env: From c33550d7717dec902306ac2bca150af2dc36e52f Mon Sep 17 00:00:00 2001 From: Jacob Cable <32874567+cabljac@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:41:31 +0000 Subject: [PATCH 033/562] feat: add Context Caching to Gemini plugins (#1297) * feat(js/plugins/googleai): add context caching docs(js/plugins/googleai): document context caching * feat(js/plugins/vertexai): add context caching * refactor(js/plugins/vertexai): rename context caching subdir * docs(js/plugins): add more context cache docs * refactor(js/plugins): tidy up context cache handlers * test(js/plugins): add context caching unit tests * chore(js/plugins/vertexai): bump vertexai node SDK --- docs/plugins/google-genai.md | 155 ++++ docs/plugins/vertex-ai.md | 92 ++- js/plugins/googleai/package.json | 2 +- .../googleai/src/context-caching/constants.ts | 29 + .../googleai/src/context-caching/index.ts | 126 +++ .../googleai/src/context-caching/types.ts | 31 + .../googleai/src/context-caching/utils.ts | 222 +++++ js/plugins/googleai/src/gemini.ts | 83 +- .../tests/context-caching/utils_test.ts | 763 ++++++++++++++++++ js/plugins/googleai/tests/gemini_test.ts | 2 +- js/plugins/vertexai/package.json | 3 +- .../vertexai/src/context-caching/constants.ts | 29 + .../vertexai/src/context-caching/index.ts | 132 +++ .../vertexai/src/context-caching/types.ts | 31 + .../vertexai/src/context-caching/utils.ts | 194 +++++ js/plugins/vertexai/src/gemini.ts | 118 ++- .../tests/context-caching/utils_test.ts | 219 +++++ js/pnpm-lock.yaml | 115 ++- js/testapps/context-caching/.gitignore | 1 + js/testapps/context-caching/README.md | 103 +++ js/testapps/context-caching/package.json | 27 + js/testapps/context-caching/src/index.ts | 122 +++ js/testapps/context-caching/tsconfig.json | 14 + js/testapps/context-caching2/.gitignore | 1 + js/testapps/context-caching2/README.md | 105 +++ js/testapps/context-caching2/package.json | 26 + js/testapps/context-caching2/src/index.ts | 97 +++ js/testapps/context-caching2/tsconfig.json | 14 + 28 files changed, 2763 insertions(+), 93 deletions(-) create mode 100644 js/plugins/googleai/src/context-caching/constants.ts create mode 100644 js/plugins/googleai/src/context-caching/index.ts create mode 100644 js/plugins/googleai/src/context-caching/types.ts create mode 100644 js/plugins/googleai/src/context-caching/utils.ts create mode 100644 js/plugins/googleai/tests/context-caching/utils_test.ts create mode 100644 js/plugins/vertexai/src/context-caching/constants.ts create mode 100644 js/plugins/vertexai/src/context-caching/index.ts create mode 100644 js/plugins/vertexai/src/context-caching/types.ts create mode 100644 js/plugins/vertexai/src/context-caching/utils.ts create mode 100644 js/plugins/vertexai/tests/context-caching/utils_test.ts create mode 100644 js/testapps/context-caching/.gitignore create mode 100644 js/testapps/context-caching/README.md create mode 100644 js/testapps/context-caching/package.json create mode 100644 js/testapps/context-caching/src/index.ts create mode 100644 js/testapps/context-caching/tsconfig.json create mode 100644 js/testapps/context-caching2/.gitignore create mode 100644 js/testapps/context-caching2/README.md create mode 100644 js/testapps/context-caching2/package.json create mode 100644 js/testapps/context-caching2/src/index.ts create mode 100644 js/testapps/context-caching2/tsconfig.json diff --git a/docs/plugins/google-genai.md b/docs/plugins/google-genai.md index 8cdb1ff0a..b758d1a94 100644 --- a/docs/plugins/google-genai.md +++ b/docs/plugins/google-genai.md @@ -133,3 +133,158 @@ const llmResponse = await ai.generate({ }), }); ``` + + + +## Context Caching + +The Google Generative AI plugin supports **context caching**, which allows models to reuse previously cached content to optimize performance and reduce latency for repetitive tasks. This feature is especially useful for conversational flows or scenarios where the model references a large body of text consistently across multiple requests. + +### How to Use Context Caching + +To enable context caching, ensure your model supports it. For example, `gemini15Flash` and `gemini15Pro` are models that support context caching. + +You can define a caching mechanism in your application like this: + +```ts +const ai = genkit({ + plugins: [googleAI()], +}); + +const llmResponse = await ai.generate({ + messages: [ + { + role: 'user', + content: [{ text: 'Here is the relevant text from War and Peace.' }], + }, + { + role: 'model', + content: [ + { + text: 'Based on War and Peace, here is some analysis of Pierre Bezukhov’s character.', + }, + ], + metadata: { + cache: { + ttlSeconds: 300, // Cache this message for 5 minutes + }, + }, + }, + ], + model: gemini15Flash, + config: { + version: 'gemini-1.5-flash-001', // Only 001 currently supports context caching + }, + prompt: 'Describe Pierre’s transformation throughout the novel.', +}); +``` + +In this setup: +- **`messages`**: Allows you to pass conversation history. +- **`metadata.cache.ttlSeconds`**: Specifies the time-to-live (TTL) for caching a specific response. + +### Example: Leveraging Large Texts with Context + +For applications referencing long documents, such as *War and Peace* or *Lord of the Rings*, you can structure your queries to reuse cached contexts: + +```ts +const fs = require('fs/promises'); + +const textContent = await fs.readFile('path/to/war_and_peace.txt', 'utf-8'); + +const llmResponse = await ai.generate({ + messages: [ + { + role: 'user', + content: [{ text: textContent }], // Include the large text as context + }, + { + role: 'model', + content: [ + { + text: 'This analysis is based on the provided text from War and Peace.', + }, + ], + metadata: { + cache: { + ttlSeconds: 300, // Cache the response to avoid reloading the full text + }, + }, + }, + ], + model: gemini15Flash, + config: { + version: 'gemini-1.5-flash-001', // Only 001 currently supports context caching + }, + prompt: 'Analyze the relationship between Pierre and Natasha.', +}); +``` + +### Caching other modes of content + +The Gemini models are multi-modal, and other modes of content are allowed to be cached as well. + +For example, to cache a long piece of video content, you must first upload using the file manager from the Google AI SDK: + +```ts +import { GoogleAIFileManager } from '@google/generative-ai/server'; + +``` + +```ts +const fileManager = new GoogleAIFileManager( + process.env.GOOGLE_GENAI_API_KEY +); + +// Upload video to Google AI using the Gemini Files API +const uploadResult = await fileManager.uploadFile(videoFilePath, { + mimeType: 'video/mp4', // Adjust according to the video format + displayName: 'Uploaded Video for Analysis', +}); + +const fileUri = uploadResult.file.uri; + +``` +Now you may configure the cache in your calls to `ai.generate`: +```ts +const analyzeVideoResponse = await ai.generate({ + messages: [ + { + role: 'user', + content: [ + { + media: { + url: fileUri, // Use the uploaded file URL + contentType: 'video/mp4', + }, + }, + ], + }, + { + role: 'model', + content: [ + { + text: 'This video seems to contain several key moments. I will analyze it now and prepare to answer your questions.', + }, + ], + // Everything up to (including) this message will be cached. + metadata: { + cache: true, + }, + }, + ], + config: { + version: 'gemini-1.5-flash-001', // Only 001 versions support context caches + }, + model: gemini15Flash, + prompt: query, +}); +``` + +### Supported Models for Context Caching + +Only specific models, such as `gemini15Flash` and `gemini15Pro`, support context caching. If an unsupported model is used, an error will be raised, indicating that caching cannot be applied. + +### Further Reading + +See more information regarding context caching on Google AI in their [documentation](https://ai.google.dev/gemini-api/docs/caching?lang=node). diff --git a/docs/plugins/vertex-ai.md b/docs/plugins/vertex-ai.md index c954a9ad0..511ba521a 100644 --- a/docs/plugins/vertex-ai.md +++ b/docs/plugins/vertex-ai.md @@ -485,4 +485,94 @@ See the code samples for: * [Vertex Vector Search + BigQuery](https://github.com/firebase/genkit/tree/main/js/testapps/vertexai-vector-search-bigquery) * [Vertex Vector Search + Firestore](https://github.com/firebase/genkit/tree/main/js/testapps/vertexai-vector-search-firestore) -* [Vertex Vector Search + a custom DB](https://github.com/firebase/genkit/tree/main/js/testapps/vertexai-vector-search-custom) \ No newline at end of file +* [Vertex Vector Search + a custom DB](https://github.com/firebase/genkit/tree/main/js/testapps/vertexai-vector-search-custom) + +## Context Caching + +The Vertex AI Genkit plugin supports **Context Caching**, which allows models to reuse previously cached content to optimize token usage when dealing with large pieces of content. This feature is especially useful for conversational flows or scenarios where the model references a large piece of content consistently across multiple requests. + +### How to Use Context Caching + +To enable context caching, ensure your model supports it. For example, `gemini15Flash` and `gemini15Pro` are models that support context caching, and you will have to specify version number `001`. + +You can define a caching mechanism in your application like this: + +```ts +const ai = genkit({ + plugins: [googleAI()], +}); + +const llmResponse = await ai.generate({ + messages: [ + { + role: 'user', + content: [{ text: 'Here is the relevant text from War and Peace.' }], + }, + { + role: 'model', + content: [ + { + text: 'Based on War and Peace, here is some analysis of Pierre Bezukhov’s character.', + }, + ], + metadata: { + cache: { + ttlSeconds: 300, // Cache this message for 5 minutes + }, + }, + }, + ], + model: gemini15Flash, + prompt: 'Describe Pierre’s transformation throughout the novel.', +}); +``` + +In this setup: +- **`messages`**: Allows you to pass conversation history. +- **`metadata.cache.ttlSeconds`**: Specifies the time-to-live (TTL) for caching a specific response. + +### Example: Leveraging Large Texts with Context + +For applications referencing long documents, such as *War and Peace* or *Lord of the Rings*, you can structure your queries to reuse cached contexts: + +```ts + +const textContent = await fs.readFile('path/to/war_and_peace.txt', 'utf-8'); + +const llmResponse = await ai.generate({ + messages: [ + { + role: 'user', + content: [{ text: textContent }], // Include the large text as context + }, + { + role: 'model', + content: [ + { + text: 'This analysis is based on the provided text from War and Peace.', + }, + ], + metadata: { + cache: { + ttlSeconds: 300, // Cache the response to avoid reloading the full text + }, + }, + }, + ], + model: gemini15Flash, + prompt: 'Analyze the relationship between Pierre and Natasha.', +}); +``` + +### Benefits of Context Caching +1. **Improved Performance**: Reduces the need for repeated processing of large inputs. +2. **Cost Efficiency**: Decreases API usage for redundant data, optimizing token consumption. +3. **Better Latency**: Speeds up response times for repeated or related queries. + +### Supported Models for Context Caching + +Only specific models, such as `gemini15Flash` and `gemini15Pro`, support context caching, and currently only on version numbers `001`. If an unsupported model is used, an error will be raised, indicating that caching cannot be applied. + +### Further Reading + +See more information regarding context caching on Vertex AI in their [documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/context-cache/context-cache-overview). diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 555ca0d36..02ad12d94 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -21,7 +21,7 @@ "build:clean": "rimraf ./lib", "build": "npm-run-all build:clean check compile", "build:watch": "tsup-node --watch", - "test": "tsx --test ./tests/*_test.ts" + "test": "tsx --test ./tests/**/*_test.ts" }, "repository": { "type": "git", diff --git a/js/plugins/googleai/src/context-caching/constants.ts b/js/plugins/googleai/src/context-caching/constants.ts new file mode 100644 index 000000000..315076a85 --- /dev/null +++ b/js/plugins/googleai/src/context-caching/constants.ts @@ -0,0 +1,29 @@ +/** + * Copyright 2024 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. + */ + +export const CONTEXT_CACHE_SUPPORTED_MODELS = [ + 'gemini-1.5-flash-001', + 'gemini-1.5-pro-001', +]; + +export const INVALID_ARGUMENT_MESSAGES = { + modelVersion: `Model version is required for context caching, supported only in ${CONTEXT_CACHE_SUPPORTED_MODELS.join(',')} models.`, + tools: 'Context caching cannot be used simultaneously with tools.', + codeExecution: + 'Context caching cannot be used simultaneously with code execution.', +}; + +export const DEFAULT_TTL = 300; diff --git a/js/plugins/googleai/src/context-caching/index.ts b/js/plugins/googleai/src/context-caching/index.ts new file mode 100644 index 000000000..7d7a86fba --- /dev/null +++ b/js/plugins/googleai/src/context-caching/index.ts @@ -0,0 +1,126 @@ +/** + * Copyright 2024 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 { CachedContent, StartChatParams } from '@google/generative-ai'; +import { + CachedContentCreateParams, + GoogleAICacheManager, +} from '@google/generative-ai/server'; +import { GenerateRequest, GenkitError, z } from 'genkit'; +import { logger } from 'genkit/logging'; +import type { CacheConfigDetails } from './types.js'; +import { + calculateTTL, + generateCacheKey, + getContentForCache, + lookupContextCache, + validateContextCacheRequest, +} from './utils.js'; + +/** + * Handles context caching and transforms the chatRequest + * @param apiKey + * @param request + * @param chatRequest + * @param modelVersion + * @returns + */ +export async function handleContextCache( + apiKey: string, + request: GenerateRequest, + chatRequest: StartChatParams, + modelVersion: string, + cacheConfigDetails: CacheConfigDetails +): Promise<{ cache: CachedContent; newChatRequest: StartChatParams }> { + const cacheManager = new GoogleAICacheManager(apiKey); + + const { cachedContent, chatRequest: newChatRequest } = getContentForCache( + request, + chatRequest, + modelVersion, + cacheConfigDetails + ); + cachedContent.model = modelVersion; + const cacheKey = generateCacheKey(cachedContent); + + cachedContent.displayName = cacheKey; + + let cache = await lookupContextCache(cacheManager, cacheKey); + logger.debug(`Cache hit: ${cache ? 'true' : 'false'}`); + + if (!cache) { + try { + logger.debug('No cache found, creating one.'); + const createParams: CachedContentCreateParams = { + ...cachedContent, + ttlSeconds: calculateTTL(cacheConfigDetails), + }; + cache = await cacheManager.create(createParams); + logger.debug(`Created new cache entry with key: ${cacheKey}`); + } catch (cacheError) { + logger.error( + `Failed to create cache with key ${cacheKey}: ${cacheError}` + ); + throw new GenkitError({ + status: 'INTERNAL', + message: `Failed to create cache: ${cacheError}`, + }); + } + } + + if (!cache) { + throw new GenkitError({ + status: 'INTERNAL', + message: 'Failed to use context cache feature', + }); + } + + return { cache, newChatRequest }; +} + +/** + * Handles cache validation, creation, and usage, transforming the chatRequest if necessary. + * @param apiKey The API key for accessing Google AI Gemini. + * @param request The generate request passed to the model. + * @param chatRequest The current chat request configuration. + * @param modelVersion The version of the model being used. + * @param cacheConfigDetails Configuration details for caching. + * @returns A transformed chat request and cache data (if applicable). + */ +export async function handleCacheIfNeeded( + apiKey: string, + request: GenerateRequest, + chatRequest: StartChatParams, + modelVersion: string, + cacheConfigDetails: CacheConfigDetails | null +): Promise<{ chatRequest: StartChatParams; cache: CachedContent | null }> { + // Skip caching if no configuration or if validation fails + if ( + !cacheConfigDetails || + !validateContextCacheRequest(request, modelVersion) + ) { + return { chatRequest, cache: null }; + } + + const { cache, newChatRequest } = await handleContextCache( + apiKey, + request, + chatRequest, + modelVersion, + cacheConfigDetails + ); + return { chatRequest: newChatRequest, cache }; +} diff --git a/js/plugins/googleai/src/context-caching/types.ts b/js/plugins/googleai/src/context-caching/types.ts new file mode 100644 index 000000000..50f186424 --- /dev/null +++ b/js/plugins/googleai/src/context-caching/types.ts @@ -0,0 +1,31 @@ +/** + * Copyright 2024 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 { z } from 'genkit'; + +export const cacheConfigSchema = z.union([ + z.boolean(), + z.object({ ttlSeconds: z.number().optional() }).passthrough(), +]); + +export type CacheConfig = z.infer; + +export const cacheConfigDetailsSchema = z.object({ + cacheConfig: cacheConfigSchema, + endOfCachedContents: z.number(), +}); + +export type CacheConfigDetails = z.infer; diff --git a/js/plugins/googleai/src/context-caching/utils.ts b/js/plugins/googleai/src/context-caching/utils.ts new file mode 100644 index 000000000..4e0965a9b --- /dev/null +++ b/js/plugins/googleai/src/context-caching/utils.ts @@ -0,0 +1,222 @@ +/** + * Copyright 2024 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 { CachedContent, StartChatParams } from '@google/generative-ai'; +import { GoogleAICacheManager } from '@google/generative-ai/server'; +import crypto from 'crypto'; +import { GenkitError, MessageData, z } from 'genkit'; +import { GenerateRequest } from 'genkit/model'; +import { + CONTEXT_CACHE_SUPPORTED_MODELS, + DEFAULT_TTL, + INVALID_ARGUMENT_MESSAGES, +} from './constants'; +import { CacheConfig, CacheConfigDetails, cacheConfigSchema } from './types'; + +/** + * Generates a SHA-256 hash to use as a cache key. + * @param request CachedContent - request object to hash + * @returns string - the generated cache key + */ +export function generateCacheKey(request: CachedContent): string { + return crypto + .createHash('sha256') + .update(JSON.stringify(request)) + .digest('hex'); +} + +/** + * Retrieves the content needed for the cache based on the chat history and config details. + */ +export function getContentForCache( + request: GenerateRequest, + chatRequest: StartChatParams, + modelVersion: string, + cacheConfigDetails: CacheConfigDetails +): { + cachedContent: CachedContent; + chatRequest: StartChatParams; + cacheConfig?: CacheConfig; +} { + // Ensure modelVersion is provided + if (!modelVersion) { + throw new Error('No model version provided for context caching'); + } + + // Ensure chatRequest has a history + if (!chatRequest.history?.length) { + throw new Error('No history provided for context caching'); + } + + // Validate the history length between request and chatRequest + validateHistoryLength(request, chatRequest); + + // Extract relevant cached content based on cacheConfigDetails + const { endOfCachedContents, cacheConfig } = cacheConfigDetails; + const cachedContent: CachedContent = { + model: modelVersion, + contents: chatRequest.history.slice(0, endOfCachedContents + 1), + }; + + // Update the chatRequest history to only include non-cached parts + chatRequest.history = chatRequest.history.slice(endOfCachedContents + 1); + + return { cachedContent, chatRequest, cacheConfig }; +} + +/** + * Validates that the request and chat request history lengths align. + * @throws GenkitError if lengths are mismatched + */ +function validateHistoryLength( + request: GenerateRequest, + chatRequest: StartChatParams +) { + if (chatRequest.history?.length !== request.messages.length - 1) { + throw new GenkitError({ + status: 'INTERNAL', + message: + 'Genkit request history and Gemini chat request history length do not match', + }); + } +} + +/** + * Looks up context cache using a cache manager and returns the found item, if any. + */ +/** + * Looks up context cache using a cache manager and returns the found item, if any. + */ +export async function lookupContextCache( + cacheManager: GoogleAICacheManager, + cacheKey: string, + maxPages = 100, + pageSize = 100 +): Promise { + let currentPage = 0; + let pageToken: string | undefined; + + try { + while (currentPage < maxPages) { + const { cachedContents, nextPageToken } = await cacheManager.list({ + pageSize, + pageToken, + }); + + // Check for the cached content by key + const found = cachedContents?.find( + (content) => content.displayName === cacheKey + ); + + if (found) return found; // Return found content + + // Stop if there's no next page + if (!nextPageToken) break; + + pageToken = nextPageToken; + currentPage++; + } + } catch (error) { + const message = + error instanceof Error ? error.message : 'Unknown Network Error'; + + throw new GenkitError({ + status: 'INTERNAL', + message: `Error looking up context cache: ${message}`, + }); + } + + return null; // Return null if not found or on error +} + +/** + * Extracts the cache configuration from the request if available. + */ +export const extractCacheConfig = ( + request: GenerateRequest +): { + cacheConfig: { ttlSeconds?: number } | boolean; + endOfCachedContents: number; +} | null => { + const endOfCachedContents = findLastIndex( + request.messages, + (message) => !!message.metadata?.cache + ); + + return endOfCachedContents === -1 + ? null + : { + endOfCachedContents, + cacheConfig: cacheConfigSchema.parse( + request.messages[endOfCachedContents].metadata?.cache + ), + }; +}; + +/** + * Validates context caching request for compatibility with model and request configurations. + */ +export function validateContextCacheRequest( + request: GenerateRequest, + modelVersion: string +): boolean { + if (!modelVersion || !CONTEXT_CACHE_SUPPORTED_MODELS.includes(modelVersion)) { + throw new GenkitError({ + status: 'INVALID_ARGUMENT', + message: INVALID_ARGUMENT_MESSAGES.modelVersion, + }); + } + if (request.tools?.length) + throw new GenkitError({ + status: 'INVALID_ARGUMENT', + message: INVALID_ARGUMENT_MESSAGES.tools, + }); + if (request.config?.codeExecution) + throw new GenkitError({ + status: 'INVALID_ARGUMENT', + message: INVALID_ARGUMENT_MESSAGES.codeExecution, + }); + + return true; +} + +/** + * Polyfill function for Array.prototype.findLastIndex for ES2015 compatibility. + */ +export function findLastIndex( + array: T[], + callback: (element: T, index: number, array: T[]) => boolean +): number { + for (let i = array.length - 1; i >= 0; i--) { + if (callback(array[i], i, array)) return i; + } + return -1; +} + +/** + * Calculates the TTL (Time-To-Live) for the cache based on cacheConfigDetails. + * @param cacheConfig - The caching configuration details. + * @returns The TTL in seconds. + */ +export function calculateTTL(cacheConfig: CacheConfigDetails): number { + if (cacheConfig.cacheConfig === true) { + return DEFAULT_TTL; + } + if (cacheConfig.cacheConfig === false) { + return 0; + } + return cacheConfig.cacheConfig.ttlSeconds || DEFAULT_TTL; +} diff --git a/js/plugins/googleai/src/gemini.ts b/js/plugins/googleai/src/gemini.ts index 81a6809e5..125a62f1e 100644 --- a/js/plugins/googleai/src/gemini.ts +++ b/js/plugins/googleai/src/gemini.ts @@ -25,6 +25,7 @@ import { Part as GeminiPart, GenerateContentResponse, GenerationConfig, + GenerativeModel, GoogleGenerativeAI, InlineDataPart, RequestOptions, @@ -61,6 +62,8 @@ import { simulateSystemPrompt, } from 'genkit/model/middleware'; import process from 'process'; +import { handleCacheIfNeeded } from './context-caching'; +import { extractCacheConfig } from './context-caching/utils'; const SafetySettingsSchema = z.object({ category: z.enum([ @@ -81,6 +84,7 @@ const SafetySettingsSchema = z.object({ export const GeminiConfigSchema = GenerationCommonConfigSchema.extend({ safetySettings: z.array(SafetySettingsSchema).optional(), codeExecution: z.union([z.boolean(), z.object({}).strict()]).optional(), + contextCache: z.boolean().optional(), functionCallingConfig: z .object({ mode: z.enum(['MODE_UNSPECIFIED', 'AUTO', 'ANY', 'NONE']).optional(), @@ -132,6 +136,8 @@ export const gemini15Flash = modelRef({ media: true, tools: true, systemRole: true, + // @ts-ignore + contextCache: true, }, versions: [ 'gemini-1.5-flash-latest', @@ -550,25 +556,13 @@ export function defineGoogleAIModel( ...request.config, }; - const client = new GoogleGenerativeAI(apiKey!).getGenerativeModel( - { - model: - requestConfig.version || - model.config?.version || - model.version || - apiModelName, - }, - options - ); - - // make a copy so that modifying the request will not produce side-effects + // Make a copy so that modifying the request will not produce side-effects const messages = [...request.messages]; if (messages.length === 0) throw new Error('No messages provided.'); // Gemini does not support messages with role system and instead expects // systemInstructions to be provided as a separate input. The first // message detected with role=system will be used for systemInstructions. - // Any additional system messages may be considered to be "exceptional". let systemInstruction: GeminiMessage | undefined = undefined; if (SUPPORTED_V15_MODELS[name]) { const systemMessage = messages.find((m) => m.role === 'system'); @@ -607,7 +601,7 @@ export function defineGoogleAIModel( }; } - // cannot use tools with json mode + // Cannot use tools with JSON mode const jsonMode = (request.output?.format === 'json' || request.output?.contentType === 'application/json') && @@ -627,7 +621,15 @@ export function defineGoogleAIModel( generationConfig.responseSchema = cleanSchema(request.output.schema); } - const chatRequest = { + const msg = toGeminiMessage(messages[messages.length - 1], model); + + const fromJSONModeScopedGeminiCandidate = ( + candidate: GeminiCandidate + ) => { + return fromGeminiCandidate(candidate, jsonMode); + }; + + let chatRequest: StartChatParams = { systemInstruction, generationConfig, tools, @@ -637,16 +639,43 @@ export function defineGoogleAIModel( .map((message) => toGeminiMessage(message, model)), safetySettings: requestConfig.safetySettings, } as StartChatParams; - const msg = toGeminiMessage(messages[messages.length - 1], model); + const modelVersion = (request.config?.version || + model.version || + name) as string; + const cacheConfigDetails = extractCacheConfig(request); + + const { chatRequest: updatedChatRequest, cache } = + await handleCacheIfNeeded( + apiKey!, + request, + chatRequest, + modelVersion, + cacheConfigDetails + ); + + const client = new GoogleGenerativeAI(apiKey!); + let genModel: GenerativeModel; + + if (cache) { + genModel = client.getGenerativeModelFromCachedContent( + cache, + { + model: modelVersion, + }, + options + ); + } else { + genModel = client.getGenerativeModel( + { + model: modelVersion, + }, + options + ); + } - const fromJSONModeScopedGeminiCandidate = ( - candidate: GeminiCandidate - ) => { - return fromGeminiCandidate(candidate, jsonMode); - }; if (streamingCallback) { - const result = await client - .startChat(chatRequest) + const result = await genModel + .startChat(updatedChatRequest) .sendMessageStream(msg.parts, options); for await (const item of result.stream) { (item as GenerateContentResponse).candidates?.forEach((candidate) => { @@ -669,17 +698,17 @@ export function defineGoogleAIModel( }); } return { - candidates: candidates?.map(fromJSONModeScopedGeminiCandidate) || [], + candidates: candidates.map(fromJSONModeScopedGeminiCandidate) || [], custom: response, }; } else { - const result = await client - .startChat(chatRequest) + const result = await genModel + .startChat(updatedChatRequest) .sendMessage(msg.parts, options); if (!result.response.candidates?.length) throw new Error('No valid candidates returned.'); const responseCandidates = - result.response.candidates?.map(fromJSONModeScopedGeminiCandidate) || + result.response.candidates.map(fromJSONModeScopedGeminiCandidate) || []; return { candidates: responseCandidates, diff --git a/js/plugins/googleai/tests/context-caching/utils_test.ts b/js/plugins/googleai/tests/context-caching/utils_test.ts new file mode 100644 index 000000000..d8465f1a9 --- /dev/null +++ b/js/plugins/googleai/tests/context-caching/utils_test.ts @@ -0,0 +1,763 @@ +/** + * Copyright 2024 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 { CachedContent, StartChatParams } from '@google/generative-ai'; +import assert from 'assert'; +import crypto from 'crypto'; +import { GenerateRequest, GenkitError } from 'genkit'; +import { describe, it } from 'node:test'; +import { + CONTEXT_CACHE_SUPPORTED_MODELS, + DEFAULT_TTL, + INVALID_ARGUMENT_MESSAGES, +} from '../../src/context-caching/constants.js'; +import { CacheConfigDetails } from '../../src/context-caching/types.js'; +import { + calculateTTL, + extractCacheConfig, + findLastIndex, + generateCacheKey, + getContentForCache, + lookupContextCache, + validateContextCacheRequest, +} from '../../src/context-caching/utils'; + +describe('generateCacheKey', () => { + it('should generate a SHA-256 hash for a given request object', () => { + const request: CachedContent = { + contents: [ + { + role: 'user', + parts: [{ text: 'Hello world' }], + }, + ], + }; + const expectedHash = crypto + .createHash('sha256') + .update(JSON.stringify(request)) + .digest('hex'); + + const result = generateCacheKey(request); + assert.strictEqual( + result, + expectedHash, + 'Generated hash does not match expected hash' + ); + }); + + it('should handle an empty `contents` array as input', () => { + const request: CachedContent = { + contents: [], + }; + const expectedHash = crypto + .createHash('sha256') + .update(JSON.stringify(request)) + .digest('hex'); + + const result = generateCacheKey(request); + assert.strictEqual( + result, + expectedHash, + 'Generated hash does not match expected hash for empty `contents` array' + ); + }); + + it('should generate different hashes for different objects', () => { + const request1: CachedContent = { + contents: [ + { + role: 'user', + parts: [{ text: 'First message' }], + }, + ], + }; + const request2: CachedContent = { + contents: [ + { + role: 'user', + parts: [{ text: 'Second message' }], + }, + ], + }; + + const hash1 = generateCacheKey(request1); + const hash2 = generateCacheKey(request2); + + assert.notStrictEqual( + hash1, + hash2, + 'Hashes for different objects should not match' + ); + }); + + it('should be consistent for the same input', () => { + const request: CachedContent = { + contents: [ + { + role: 'user', + parts: [{ text: 'Consistent message' }], + }, + ], + }; + const hash1 = generateCacheKey(request); + const hash2 = generateCacheKey(request); + assert.strictEqual(hash1, hash2, 'Hashes for the same object should match'); + }); + + it('should handle nested parts correctly', () => { + const request: CachedContent = { + contents: [ + { + role: 'user', + parts: [ + { text: 'Outer part', inlineData: undefined }, + { + text: 'Nested part', + functionCall: undefined, + }, + ], + }, + ], + }; + const expectedHash = crypto + .createHash('sha256') + .update(JSON.stringify(request)) + .digest('hex'); + + const result = generateCacheKey(request); + assert.strictEqual( + result, + expectedHash, + 'Generated hash does not match expected hash for nested parts' + ); + }); + + it('should include optional properties if provided', () => { + const request: CachedContent = { + contents: [ + { + role: 'assistant', + parts: [{ text: 'Hello from the assistant' }], + }, + ], + model: 'gpt-4', + displayName: 'test-cache', + }; + const expectedHash = crypto + .createHash('sha256') + .update(JSON.stringify(request)) + .digest('hex'); + + const result = generateCacheKey(request); + assert.strictEqual( + result, + expectedHash, + 'Generated hash does not match expected hash with optional properties' + ); + }); + it('should handle malformed input gracefully', () => { + const request: any = {}; // Malformed input + const result = generateCacheKey(request); + assert.ok(result, 'Should return a valid hash even for malformed input'); + }); +}); + +describe('getContentForCache', () => { + it('should correctly retrieve cached content and updated chat request', () => { + const request: GenerateRequest = { + messages: [ + { role: 'system', content: [{ text: 'System message' }] }, + { role: 'user', content: [{ text: 'Hello!' }] }, + { role: 'model', content: [{ text: 'Response' }] }, // Added an assistant response + ], + }; + const chatRequest: StartChatParams = { + history: [ + { role: 'system', parts: [{ text: 'System message' }] }, + { role: 'user', parts: [{ text: 'User message' }] }, + ], + }; + const modelVersion = 'gpt-4'; + const cacheConfigDetails: CacheConfigDetails = { + endOfCachedContents: 0, + cacheConfig: { ttlSeconds: 300 }, + }; + + const result = getContentForCache( + request, + chatRequest, + modelVersion, + cacheConfigDetails + ); + + assert.deepStrictEqual(result.cachedContent, { + model: modelVersion, + contents: [{ role: 'system', parts: [{ text: 'System message' }] }], + }); + assert.deepStrictEqual(result.chatRequest.history, [ + { role: 'user', parts: [{ text: 'User message' }] }, + ]); + assert.deepStrictEqual(result.cacheConfig, { ttlSeconds: 300 }); + }); + + it('should work correctly with no cacheConfigDetails', () => { + const request: GenerateRequest = { + messages: [ + { role: 'system', content: [{ text: 'System message' }] }, + { role: 'user', content: [{ text: 'Hello!' }] }, + ], + }; + const chatRequest: StartChatParams = { + history: [{ role: 'system', parts: [{ text: 'System message' }] }], + }; + const modelVersion = 'gpt-4'; + const cacheConfigDetails: CacheConfigDetails = { + endOfCachedContents: 0, + cacheConfig: true, + }; + + const result = getContentForCache( + request, + chatRequest, + modelVersion, + cacheConfigDetails + ); + + assert.deepStrictEqual(result.cachedContent, { + model: modelVersion, + contents: [{ role: 'system', parts: [{ text: 'System message' }] }], + }); + assert.deepStrictEqual(result.chatRequest.history, []); + assert.strictEqual(result.cacheConfig, true); + }); + + it('should throw an error if modelVersion is missing', () => { + const request: GenerateRequest = { + messages: [{ role: 'user', content: [{ text: 'Hello!' }] }], + }; + const chatRequest: StartChatParams = { + history: [{ role: 'user', parts: [{ text: 'Hello!' }] }], + }; + const cacheConfigDetails: CacheConfigDetails = { + endOfCachedContents: 0, + cacheConfig: true, + }; + + assert.throws( + () => { + getContentForCache(request, chatRequest, '', cacheConfigDetails); + }, + (error: any) => + error instanceof Error && + error.message === 'No model version provided for context caching', + 'Expected an error about missing model version' + ); + }); + + it('should throw an error if endOfCachedContents exceeds history length', () => { + const request: GenerateRequest = { + messages: [{ role: 'user', content: [{ text: 'Hello!' }] }], + }; + const chatRequest: StartChatParams = { + history: [{ role: 'user', parts: [{ text: 'Hello!' }] }], + }; + const modelVersion = 'gpt-4'; + const cacheConfigDetails: CacheConfigDetails = { + endOfCachedContents: 5, // Exceeds history length + cacheConfig: true, + }; + + assert.throws( + () => { + getContentForCache( + request, + chatRequest, + modelVersion, + cacheConfigDetails + ); + }, + (error: any) => + error instanceof Error && + error.message === + 'INTERNAL: Genkit request history and Gemini chat request history length do not match', + 'Expected error for out-of-bounds endOfCachedContents' + ); + }); +}); + +describe('findLastIndex', () => { + it('should return the index of the last element that satisfies the callback', () => { + const array = [1, 2, 3, 4, 5, 6]; + const result = findLastIndex(array, (element) => element % 2 === 0); + assert.strictEqual(result, 5, 'Last even number is at index 5'); + }); + + it('should return -1 if no elements satisfy the callback', () => { + const array = [1, 3, 5, 7]; + const result = findLastIndex(array, (element) => element % 2 === 0); + assert.strictEqual(result, -1, 'No even numbers, so result is -1'); + }); + + it('should handle an empty array and return -1', () => { + const array: number[] = []; + const result = findLastIndex(array, () => true); + assert.strictEqual(result, -1, 'Empty array should return -1'); + }); + + it('should return the index of the last element matching a complex condition', () => { + const array = [ + { id: 1, value: 10 }, + { id: 2, value: 15 }, + { id: 3, value: 10 }, + { id: 4, value: 20 }, + ]; + const result = findLastIndex(array, (element) => element.value === 10); + assert.strictEqual(result, 2, 'Last object with value 10 is at index 2'); + }); + + it('should return the index of the last element when multiple match', () => { + const array = [1, 2, 3, 4, 5, 6]; + const result = findLastIndex(array, (element) => element > 2); + assert.strictEqual(result, 5, 'Last element greater than 2 is at index 5'); + }); + + it('should pass the correct arguments to the callback', () => { + const array = [1, 2, 3]; + const indices: number[] = []; + const result = findLastIndex(array, (element, index) => { + indices.push(index); // Collect indices in reverse order + return false; // Ensure all elements are checked + }); + assert.deepStrictEqual( + indices, + [2, 1, 0], + 'Callback should check indices in reverse order' + ); + assert.strictEqual(result, -1, 'No matching element, result should be -1'); + }); + + it('should handle an array with one element', () => { + const array = [42]; + const result = findLastIndex(array, (element) => element === 42); + assert.strictEqual( + result, + 0, + 'Single matching element should return index 0' + ); + }); + + it('should handle an array with all elements matching the condition', () => { + const array = [5, 5, 5, 5]; + const result = findLastIndex(array, (element) => element === 5); + assert.strictEqual( + result, + 3, + 'Last element matching condition is at index 3' + ); + }); +}); + +describe('lookupContextCache', () => { + it('should return the cached content if found on the first page', async () => { + const mockCacheManager = { + list: async ({ + pageSize, + pageToken, + }: { + pageSize: number; + pageToken?: string; + }) => ({ + cachedContents: [ + { displayName: 'key1', data: 'value1' }, + { displayName: 'key2', data: 'value2' }, + ], + nextPageToken: undefined, + }), + }; + + const result = await lookupContextCache(mockCacheManager as any, 'key1'); + assert.deepStrictEqual(result, { displayName: 'key1', data: 'value1' }); + }); + + it('should return the cached content if found on subsequent pages', async () => { + const mockCacheManager = { + list: async ({ + pageSize, + pageToken, + }: { + pageSize: number; + pageToken?: string; + }) => { + if (!pageToken) { + return { + cachedContents: [{ displayName: 'key1', data: 'value1' }], + nextPageToken: 'page2', + }; + } + if (pageToken === 'page2') { + return { + cachedContents: [{ displayName: 'key2', data: 'value2' }], + nextPageToken: undefined, + }; + } + return { cachedContents: [], nextPageToken: undefined }; + }, + }; + + const result = await lookupContextCache(mockCacheManager as any, 'key2'); + assert.deepStrictEqual(result, { displayName: 'key2', data: 'value2' }); + }); + + it('should return null if the cached content is not found', async () => { + const mockCacheManager = { + list: async ({ + pageSize, + pageToken, + }: { + pageSize: number; + pageToken?: string; + }) => ({ + cachedContents: [ + { displayName: 'key1', data: 'value1' }, + { displayName: 'key3', data: 'value3' }, + ], + nextPageToken: undefined, + }), + }; + + const result = await lookupContextCache( + mockCacheManager as any, + 'nonexistent-key' + ); + assert.strictEqual(result, null); + }); + + it('should respect the maxPages limit and return null if not found', async () => { + const mockCacheManager = { + list: async ({ + pageSize, + pageToken, + }: { + pageSize: number; + pageToken?: string; + }) => ({ + cachedContents: [{ displayName: `key${pageToken}` }], + nextPageToken: + pageToken === 'page99' + ? undefined + : `page${parseInt(pageToken || '1') + 1}`, + }), + }; + + const result = await lookupContextCache( + mockCacheManager as any, + 'key100', + 50 + ); // Limit to 50 pages + assert.strictEqual(result, null); + }); + + it('should handle an empty response gracefully', async () => { + const mockCacheManager = { + list: async ({ + pageSize, + pageToken, + }: { + pageSize: number; + pageToken?: string; + }) => ({ + cachedContents: [], + nextPageToken: undefined, + }), + }; + + const result = await lookupContextCache(mockCacheManager as any, 'key1'); + assert.strictEqual(result, null); + }); + + it('should stop searching if no nextPageToken is provided', async () => { + const mockCacheManager = { + list: async ({ + pageSize, + pageToken, + }: { + pageSize: number; + pageToken?: string; + }) => ({ + cachedContents: [{ displayName: 'key1', data: 'value1' }], + nextPageToken: undefined, + }), + }; + + const result = await lookupContextCache(mockCacheManager as any, 'key2'); + assert.strictEqual(result, null); + }); + + it('should throw a GenkitError with the correct status and message on a network error', async () => { + const mockCacheManager = { + list: async () => { + throw new Error('Network Error'); + }, + }; + + try { + await lookupContextCache(mockCacheManager as any, 'key1'); + assert.fail('Expected lookupContextCache to throw a GenkitError'); + } catch (error) { + assert.ok( + error instanceof GenkitError, + 'Error should be an instance of GenkitError' + ); + assert.strictEqual( + error.status, + 'INTERNAL', + 'Error status should be "INTERNAL"' + ); + assert.strictEqual( + error.message, + 'INTERNAL: Error looking up context cache: Network Error', + 'Error message should contain the network error details' + ); + } + }); + + it('should throw a GenkitError with "Unknown Network Error" if error message is missing', async () => { + const mockCacheManager = { + list: async () => { + throw {}; // Simulate an unknown error object + }, + }; + + try { + await lookupContextCache(mockCacheManager as any, 'key1'); + assert.fail('Expected lookupContextCache to throw a GenkitError'); + } catch (error) { + assert.ok( + error instanceof GenkitError, + 'Error should be an instance of GenkitError' + ); + assert.strictEqual( + error.status, + 'INTERNAL', + 'Error status should be "INTERNAL"' + ); + assert.strictEqual( + error.message, + 'INTERNAL: Error looking up context cache: Unknown Network Error', + 'Error message should indicate an unknown network error' + ); + } + }); +}); + +describe('validateContextCacheRequest', () => { + it('should throw an error for empty modelVersion', () => { + const request: GenerateRequest = { + messages: [], + config: {}, + }; + assert.throws( + () => validateContextCacheRequest(request, ''), + (error: any) => + error instanceof GenkitError && + error.status === 'INVALID_ARGUMENT' && + error.message === + 'INVALID_ARGUMENT: ' + INVALID_ARGUMENT_MESSAGES.modelVersion + ); + }); + + it('should return true for valid model and request', () => { + const request: GenerateRequest = { + messages: [], + config: {}, + }; + const modelVersion = 'supported-model'; + CONTEXT_CACHE_SUPPORTED_MODELS.push(modelVersion); + + const result = validateContextCacheRequest(request, modelVersion); + assert.strictEqual(result, true); + }); + + it('should throw an error for unsupported model version', () => { + const request: GenerateRequest = { + messages: [], + config: {}, + }; + + const unsupportedModelVersion = 'unsupported-model'; + + // Reset and populate the supported models list + CONTEXT_CACHE_SUPPORTED_MODELS.length = 0; + CONTEXT_CACHE_SUPPORTED_MODELS.push( + 'gemini-1.5-flash-001', + 'gemini-1.5-pro-001' + ); + + assert.throws( + () => validateContextCacheRequest(request, unsupportedModelVersion), + (error: any) => { + if (!(error instanceof GenkitError)) { + console.error('Error is not an instance of GenkitError:', error); + return false; + } + + // Updated expected message with "INVALID_ARGUMENT:" prefix + const expectedMessage = + 'INVALID_ARGUMENT: Model version is required for context caching, supported only in gemini-1.5-flash-001,gemini-1.5-pro-001 models.'; + return ( + error.status === 'INVALID_ARGUMENT' && + error.message === expectedMessage + ); + }, + 'Expected GenkitError with INVALID_ARGUMENT status and correct message' + ); + }); + + it('should throw an error if tools are present in the request', () => { + const request: GenerateRequest = { + messages: [], + tools: [{ name: 'test-tool', description: 'Test tool' }], + }; + const modelVersion = 'supported-model'; + CONTEXT_CACHE_SUPPORTED_MODELS.push(modelVersion); + + assert.throws( + () => validateContextCacheRequest(request, modelVersion), + (error: any) => { + if (!(error instanceof GenkitError)) { + console.error('Error is not an instance of GenkitError:', error); + return false; + } + + // Add "INVALID_ARGUMENT:" prefix to the expected message + const expectedMessage = + 'INVALID_ARGUMENT: Context caching cannot be used simultaneously with tools.'; + return ( + error.status === 'INVALID_ARGUMENT' && + error.message === expectedMessage + ); + }, + 'Expected GenkitError with INVALID_ARGUMENT status and correct message' + ); + }); + + it('should throw an error if code execution is enabled in the request', () => { + const request: GenerateRequest = { + messages: [], + config: { codeExecution: true }, + }; + const modelVersion = 'supported-model'; + CONTEXT_CACHE_SUPPORTED_MODELS.push(modelVersion); + + assert.throws( + () => validateContextCacheRequest(request, modelVersion), + (error: any) => { + if (!(error instanceof GenkitError)) { + console.error('Error is not an instance of GenkitError:', error); + return false; + } + + // Add "INVALID_ARGUMENT:" prefix to the expected message + const expectedMessage = + 'INVALID_ARGUMENT: Context caching cannot be used simultaneously with code execution.'; + return ( + error.status === 'INVALID_ARGUMENT' && + error.message === expectedMessage + ); + }, + 'Expected GenkitError with INVALID_ARGUMENT status and correct message' + ); + }); +}); + +describe('extractCacheConfig', () => { + it('should return null if no metadata.cache is present', () => { + const request: GenerateRequest = { + messages: [{ metadata: {}, role: 'user', content: [{ text: 'Hello!' }] }], + }; + const result = extractCacheConfig(request); + assert.strictEqual(result, null); + }); + + it('should correctly extract cache config when metadata.cache is present', () => { + const request: GenerateRequest = { + messages: [ + { + metadata: { cache: { ttlSeconds: 300 } }, + role: 'user', + content: [{ text: 'Hello!' }], + }, + { metadata: {}, role: 'model', content: [{ text: 'Response' }] }, + ], + }; + const result = extractCacheConfig(request); + assert.deepStrictEqual(result, { + endOfCachedContents: 0, + cacheConfig: { ttlSeconds: 300 }, + }); + }); + + it('should handle invalid metadata.cache structures gracefully', () => { + const request: GenerateRequest = { + messages: [ + { + metadata: { cache: 'invalid' }, + role: 'user', + content: [{ text: 'Hello!' }], + }, + ], + }; + assert.throws(() => extractCacheConfig(request)); + }); +}); + +describe('calculateTTL', () => { + it('should return the default TTL when cacheConfig is true', () => { + const cacheConfigDetails: CacheConfigDetails = { + cacheConfig: true, + endOfCachedContents: 0, + }; + const result = calculateTTL(cacheConfigDetails); + assert.strictEqual(result, DEFAULT_TTL); + }); + + it('should return 0 when cacheConfig is false', () => { + const cacheConfigDetails: CacheConfigDetails = { + cacheConfig: false, + endOfCachedContents: 0, + }; + const result = calculateTTL(cacheConfigDetails); + assert.strictEqual(result, 0); + }); + + it('should return the specified ttlSeconds value when present', () => { + const cacheConfigDetails: CacheConfigDetails = { + cacheConfig: { ttlSeconds: 500 }, + endOfCachedContents: 0, + }; + const result = calculateTTL(cacheConfigDetails); + assert.strictEqual(result, 500); + }); + + it('should return the default TTL when ttlSeconds is missing', () => { + const cacheConfigDetails: CacheConfigDetails = { + cacheConfig: {}, + endOfCachedContents: 0, + }; + const result = calculateTTL(cacheConfigDetails); + assert.strictEqual(result, DEFAULT_TTL); + }); +}); diff --git a/js/plugins/googleai/tests/gemini_test.ts b/js/plugins/googleai/tests/gemini_test.ts index 0feeb5175..8f5549a81 100644 --- a/js/plugins/googleai/tests/gemini_test.ts +++ b/js/plugins/googleai/tests/gemini_test.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { MessageData } from '@genkit-ai/ai/model'; import { GenerateContentCandidate } from '@google/generative-ai'; +import { MessageData } from 'genkit/model'; import assert from 'node:assert'; import { describe, it } from 'node:test'; import { diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index eaef30862..7f651e2e3 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -38,8 +38,8 @@ "@anthropic-ai/sdk": "^0.24.3", "@anthropic-ai/vertex-sdk": "^0.4.0", "@google-cloud/aiplatform": "^3.23.0", - "@google-cloud/vertexai": "^1.9.0", "@mistralai/mistralai-gcp": "^1.3.5", + "@google-cloud/vertexai": "^1.9.2", "google-auth-library": "^9.14.2", "googleapis": "^140.0.1", "node-fetch": "^3.3.2", @@ -54,6 +54,7 @@ }, "devDependencies": { "@types/node": "^20.11.16", + "google-gax": "^4.4.1", "npm-run-all": "^4.1.5", "rimraf": "^6.0.1", "tsup": "^8.3.5", diff --git a/js/plugins/vertexai/src/context-caching/constants.ts b/js/plugins/vertexai/src/context-caching/constants.ts new file mode 100644 index 000000000..315076a85 --- /dev/null +++ b/js/plugins/vertexai/src/context-caching/constants.ts @@ -0,0 +1,29 @@ +/** + * Copyright 2024 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. + */ + +export const CONTEXT_CACHE_SUPPORTED_MODELS = [ + 'gemini-1.5-flash-001', + 'gemini-1.5-pro-001', +]; + +export const INVALID_ARGUMENT_MESSAGES = { + modelVersion: `Model version is required for context caching, supported only in ${CONTEXT_CACHE_SUPPORTED_MODELS.join(',')} models.`, + tools: 'Context caching cannot be used simultaneously with tools.', + codeExecution: + 'Context caching cannot be used simultaneously with code execution.', +}; + +export const DEFAULT_TTL = 300; diff --git a/js/plugins/vertexai/src/context-caching/index.ts b/js/plugins/vertexai/src/context-caching/index.ts new file mode 100644 index 000000000..58f992c19 --- /dev/null +++ b/js/plugins/vertexai/src/context-caching/index.ts @@ -0,0 +1,132 @@ +/** + * @license + * Copyright 2024 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + */ + +import { CachedContent, StartChatParams } from '@google-cloud/vertexai'; +import { + ApiClient, + CachedContents, +} from '@google-cloud/vertexai/build/src/resources'; +import { GenerateRequest, GenkitError, z } from 'genkit'; +import { logger } from 'genkit/logging'; +import type { CacheConfigDetails } from './types.js'; +import { + calculateTTL, + generateCacheKey, + getContentForCache, + lookupContextCache, + validateContextCacheRequest, +} from './utils.js'; + +/** + * Handles context caching and transforms the chatRequest for Vertex AI. + * @param apiKey + * @param request + * @param chatRequest + * @param modelVersion + * @returns + */ +export async function handleContextCache( + apiClient: ApiClient, + request: GenerateRequest, + chatRequest: StartChatParams, + modelVersion: string, + cacheConfigDetails: CacheConfigDetails +): Promise<{ cache: CachedContent; newChatRequest: StartChatParams }> { + const cachedContentsClient = new CachedContents(apiClient); + + const { cachedContent, chatRequest: newChatRequest } = getContentForCache( + request, + chatRequest, + modelVersion, + cacheConfigDetails + ); + cachedContent.model = modelVersion; + const cacheKey = generateCacheKey(cachedContent); + + cachedContent.displayName = cacheKey; + + let cache; + try { + cache = await lookupContextCache(cachedContentsClient, cacheKey); + logger.debug(`Cache hit: ${cache ? 'true' : 'false'}`); + } catch (error) { + logger.debug('No cache found, creating one.'); + } + + if (!cache) { + try { + const createParams: CachedContent = { + ...cachedContent, + // TODO: make this neater - idk why they chose to stringify the ttl... + ttl: JSON.stringify(calculateTTL(cacheConfigDetails)) + 's', + }; + cache = await cachedContentsClient.create(createParams); + logger.debug(`Created new cache entry with key: ${cacheKey}`); + } catch (cacheError) { + logger.error( + `Failed to create cache with key ${cacheKey}: ${cacheError}` + ); + throw new GenkitError({ + status: 'INTERNAL', + message: `Failed to create cache: ${cacheError}`, + }); + } + } + + if (!cache) { + throw new GenkitError({ + status: 'INTERNAL', + message: 'Failed to use context cache feature', + }); + } + // This isn't necessary, but it's nice to have for debugging purposes. + newChatRequest.cachedContent = cache.name; + + return { cache, newChatRequest }; +} + +/** + * Handles cache validation, creation, and usage, transforming the chatRequest if necessary. + * @param apiClient The API client for Vertex AI. + * @param options Plugin options containing project details and auth. + * @param request The generate request passed to the model. + * @param chatRequest The current chat request configuration. + * @param modelVersion The version of the model being used. + * @param cacheConfigDetails Configuration details for caching. + * @returns A transformed chat request and cache data (if applicable). + */ +export async function handleCacheIfNeeded( + apiClient: ApiClient, + request: GenerateRequest, + chatRequest: StartChatParams, + modelVersion: string, + cacheConfigDetails: CacheConfigDetails | null +): Promise<{ chatRequest: StartChatParams; cache: CachedContent | null }> { + if ( + !cacheConfigDetails || + !validateContextCacheRequest(request, modelVersion) + ) { + return { chatRequest, cache: null }; + } + + const { cache, newChatRequest } = await handleContextCache( + apiClient, + request, + chatRequest, + modelVersion, + cacheConfigDetails + ); + return { chatRequest: newChatRequest, cache }; +} diff --git a/js/plugins/vertexai/src/context-caching/types.ts b/js/plugins/vertexai/src/context-caching/types.ts new file mode 100644 index 000000000..50f186424 --- /dev/null +++ b/js/plugins/vertexai/src/context-caching/types.ts @@ -0,0 +1,31 @@ +/** + * Copyright 2024 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 { z } from 'genkit'; + +export const cacheConfigSchema = z.union([ + z.boolean(), + z.object({ ttlSeconds: z.number().optional() }).passthrough(), +]); + +export type CacheConfig = z.infer; + +export const cacheConfigDetailsSchema = z.object({ + cacheConfig: cacheConfigSchema, + endOfCachedContents: z.number(), +}); + +export type CacheConfigDetails = z.infer; diff --git a/js/plugins/vertexai/src/context-caching/utils.ts b/js/plugins/vertexai/src/context-caching/utils.ts new file mode 100644 index 000000000..f2aab6a74 --- /dev/null +++ b/js/plugins/vertexai/src/context-caching/utils.ts @@ -0,0 +1,194 @@ +/** + * Copyright 2024 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 { CachedContent, StartChatParams } from '@google-cloud/vertexai'; +import { CachedContents } from '@google-cloud/vertexai/build/src/resources'; +import crypto from 'crypto'; +import { GenkitError, MessageData, z } from 'genkit'; +import { GenerateRequest } from 'genkit/model'; +import { + CONTEXT_CACHE_SUPPORTED_MODELS, + DEFAULT_TTL, + INVALID_ARGUMENT_MESSAGES, +} from './constants'; +import { CacheConfig, CacheConfigDetails, cacheConfigSchema } from './types'; + +/** + * Generates a SHA-256 hash to use as a cache key. + * @param request CachedContent - request object to hash + * @returns string - the generated cache key + */ +export function generateCacheKey(request: CachedContent): string { + return crypto + .createHash('sha256') + .update(JSON.stringify(request)) + .digest('hex'); +} + +/** + * Retrieves the content needed for the cache based on the chat history and config details. + */ +export function getContentForCache( + request: GenerateRequest, + chatRequest: StartChatParams, + modelVersion: string, + cacheConfigDetails: CacheConfigDetails +): { + cachedContent: CachedContent; + chatRequest: StartChatParams; + cacheConfig?: CacheConfig; +} { + if (!chatRequest.history?.length) { + throw new Error('No history provided for context caching'); + } + + validateHistoryLength(request, chatRequest); + + const { endOfCachedContents, cacheConfig } = cacheConfigDetails; + const cachedContent: CachedContent = { + model: modelVersion, + contents: chatRequest.history.slice(0, endOfCachedContents + 1), + }; + chatRequest.history = chatRequest.history.slice(endOfCachedContents + 1); + + return { cachedContent, chatRequest, cacheConfig }; +} + +/** + * Validates that the request and chat request history lengths align. + * @throws GenkitError if lengths are mismatched + */ +function validateHistoryLength( + request: GenerateRequest, + chatRequest: StartChatParams +) { + if (chatRequest.history?.length !== request.messages.length - 1) { + throw new GenkitError({ + status: 'INTERNAL', + message: + 'Genkit request history and Gemini chat request history length do not match', + }); + } +} + +/** + * Looks up context cache using a cache manager and returns the found item, if any. + */ +export async function lookupContextCache( + cacheManager: CachedContents, + cacheKey: string, + maxPages = 100, + pageSize = 100 +) { + let currentPage = 0; + let pageToken: string | undefined; + + while (currentPage < maxPages) { + const { cachedContents, nextPageToken } = await cacheManager.list( + pageSize, + pageToken + ); + const found = cachedContents?.find( + (content) => content.displayName === cacheKey + ); + + if (found) return found; + if (!nextPageToken) break; + + pageToken = nextPageToken; + currentPage++; + } + return null; +} + +/** + * Extracts the cache configuration from the request if available. + */ +export const extractCacheConfig = ( + request: GenerateRequest +): { + cacheConfig: { ttlSeconds?: number } | boolean; + endOfCachedContents: number; +} | null => { + const endOfCachedContents = findLastIndex( + request.messages, + (message) => !!message.metadata?.cache + ); + + return endOfCachedContents === -1 + ? null + : { + endOfCachedContents, + cacheConfig: cacheConfigSchema.parse( + request.messages[endOfCachedContents].metadata?.cache + ), + }; +}; + +/** + * Validates context caching request for compatibility with model and request configurations. + */ +export function validateContextCacheRequest( + request: GenerateRequest, + modelVersion: string +): boolean { + if (!modelVersion || !CONTEXT_CACHE_SUPPORTED_MODELS.includes(modelVersion)) { + throw new GenkitError({ + status: 'INVALID_ARGUMENT', + message: INVALID_ARGUMENT_MESSAGES.modelVersion, + }); + } + if (request.tools?.length) + throw new GenkitError({ + status: 'INVALID_ARGUMENT', + message: INVALID_ARGUMENT_MESSAGES.tools, + }); + if (request.config?.codeExecution) + throw new GenkitError({ + status: 'INVALID_ARGUMENT', + message: INVALID_ARGUMENT_MESSAGES.codeExecution, + }); + + return true; +} + +/** + * Polyfill function for Array.prototype.findLastIndex for ES2015 compatibility. + */ +function findLastIndex( + array: T[], + callback: (element: T, index: number, array: T[]) => boolean +): number { + for (let i = array.length - 1; i >= 0; i--) { + if (callback(array[i], i, array)) return i; + } + return -1; +} + +/** + * Calculates the TTL (Time-To-Live) for the cache based on cacheConfigDetails. + * @param cacheConfig - The caching configuration details. + * @returns The TTL in seconds. + */ +export function calculateTTL(cacheConfig: CacheConfigDetails): number { + if (cacheConfig.cacheConfig === true) { + return DEFAULT_TTL; + } + if (cacheConfig.cacheConfig === false) { + return 0; + } + return cacheConfig.cacheConfig.ttlSeconds || DEFAULT_TTL; +} diff --git a/js/plugins/vertexai/src/gemini.ts b/js/plugins/vertexai/src/gemini.ts index 574698763..a9e5f2529 100644 --- a/js/plugins/vertexai/src/gemini.ts +++ b/js/plugins/vertexai/src/gemini.ts @@ -22,13 +22,15 @@ import { Part as GeminiPart, GenerateContentCandidate, GenerateContentResponse, - GenerateContentResult, + GenerativeModelPreview, HarmBlockThreshold, HarmCategory, StartChatParams, ToolConfig, VertexAI, + type GoogleSearchRetrieval, } from '@google-cloud/vertexai'; +import { ApiClient } from '@google-cloud/vertexai/build/src/resources/index.js'; import { GENKIT_CLIENT_HEADER, Genkit, JSONSchema, z } from 'genkit'; import { CandidateData, @@ -48,7 +50,10 @@ import { downloadRequestMedia, simulateSystemPrompt, } from 'genkit/model/middleware'; +import { GoogleAuth } from 'google-auth-library'; import { PluginOptions } from './common/types.js'; +import { handleCacheIfNeeded } from './context-caching/index.js'; +import { extractCacheConfig } from './context-caching/utils.js'; const SafetySettingsSchema = z.object({ category: z.nativeEnum(HarmCategory), @@ -469,23 +474,12 @@ export function defineGeminiModel( }, async (request, streamingCallback) => { const vertex = vertexClientFactory(request); - const client = vertex.preview.getGenerativeModel( - { - model: request.config?.version || model.version || name, - }, - { - apiClient: GENKIT_CLIENT_HEADER, - } - ); - // make a copy so that modifying the request will not produce side-effects + // Make a copy of messages to avoid side-effects const messages = [...request.messages]; if (messages.length === 0) throw new Error('No messages provided.'); - // Gemini does not support messages with role system and instead expects - // systemInstructions to be provided as a separate input. The first - // message detected with role=system will be used for systemInstructions. - // Any additional system messages may be considered to be "exceptional". + // Handle system instructions separately let systemInstruction: Content | undefined = undefined; if (SUPPORTED_V15_MODELS[name]) { const systemMessage = messages.find((m) => m.role === 'system'); @@ -496,7 +490,7 @@ export function defineGeminiModel( } const tools = request.tools?.length - ? [{ functionDeclarations: request.tools?.map(toGeminiTool) }] + ? [{ functionDeclarations: request.tools.map(toGeminiTool) }] : []; let toolConfig: ToolConfig | undefined; @@ -511,12 +505,13 @@ export function defineGeminiModel( }, }; } + // Cannot use tools and function calling at the same time const jsonMode = (request.output?.format === 'json' || !!request.output?.schema) && tools.length === 0; - const chatRequest: StartChatParams = { + let chatRequest: StartChatParams = { systemInstruction, tools, toolConfig, @@ -535,19 +530,44 @@ export function defineGeminiModel( safetySettings: request.config?.safetySettings, }; + // Handle cache + const modelVersion = (request.config?.version || + model.version || + name) as string; + const cacheConfigDetails = extractCacheConfig(request); + + const apiClient = new ApiClient( + options.projectId!, + options.location, + 'v1beta1', + new GoogleAuth(options.googleAuth!) + ); + + const { chatRequest: updatedChatRequest, cache } = + await handleCacheIfNeeded( + apiClient, + request, + chatRequest, + modelVersion, + cacheConfigDetails + ); + + let genModel: GenerativeModelPreview; + if (jsonMode && request.output?.constrained) { - chatRequest.generationConfig!.responseSchema = cleanSchema( + updatedChatRequest.generationConfig!.responseSchema = cleanSchema( request.output.schema ); } if (request.config?.googleSearchRetrieval) { - chatRequest.tools?.push({ - googleSearchRetrieval: request.config.googleSearchRetrieval, + updatedChatRequest.tools?.push({ + googleSearchRetrieval: request.config + .googleSearchRetrieval as GoogleSearchRetrieval, }); } + if (request.config?.vertexRetrieval) { - // https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/ground-gemini#ground-gemini const vertexRetrieval = request.config.vertexRetrieval; const _projectId = vertexRetrieval.datastore.projectId || options.projectId; @@ -555,7 +575,7 @@ export function defineGeminiModel( vertexRetrieval.datastore.location || options.location; const _dataStoreId = vertexRetrieval.datastore.dataStoreId; const datastore = `projects/${_projectId}/locations/${_location}/collections/default_collection/dataStores/${_dataStoreId}`; - chatRequest.tools?.push({ + updatedChatRequest.tools?.push({ retrieval: { vertexAiSearch: { datastore, @@ -564,11 +584,36 @@ export function defineGeminiModel( }, }); } + const msg = toGeminiMessage(messages[messages.length - 1], model); + + if (cache) { + genModel = vertex.preview.getGenerativeModelFromCachedContent( + cache, + { + model: modelVersion, + }, + { + apiClient: GENKIT_CLIENT_HEADER, + } + ); + } else { + genModel = vertex.preview.getGenerativeModel( + { + model: modelVersion, + }, + { + apiClient: GENKIT_CLIENT_HEADER, + } + ); + } + + // Handle streaming and non-streaming responses if (streamingCallback) { - const result = await client - .startChat(chatRequest) + const result = await genModel + .startChat(updatedChatRequest) .sendMessageStream(msg.parts); + for await (const item of result.stream) { (item as GenerateContentResponse).candidates?.forEach((candidate) => { const c = fromGeminiCandidate(candidate, jsonMode); @@ -578,30 +623,31 @@ export function defineGeminiModel( }); }); } + const response = await result.response; if (!response.candidates?.length) { throw new Error('No valid candidates returned.'); } + return { - candidates: - response.candidates?.map((c) => fromGeminiCandidate(c, jsonMode)) || - [], + candidates: response.candidates.map((c) => + fromGeminiCandidate(c, jsonMode) + ), custom: response, }; } else { - let result: GenerateContentResult | undefined; - try { - result = await client.startChat(chatRequest).sendMessage(msg.parts); - } catch (err) { - throw new Error(`Vertex response generation failed: ${err}`); - } + const result = await genModel + .startChat(updatedChatRequest) + .sendMessage(msg.parts); + if (!result?.response.candidates?.length) { throw new Error('No valid candidates returned.'); } - const responseCandidates = - result.response.candidates?.map((c) => - fromGeminiCandidate(c, jsonMode) - ) || []; + + const responseCandidates = result.response.candidates.map((c) => + fromGeminiCandidate(c, jsonMode) + ); + return { candidates: responseCandidates, custom: result.response, diff --git a/js/plugins/vertexai/tests/context-caching/utils_test.ts b/js/plugins/vertexai/tests/context-caching/utils_test.ts new file mode 100644 index 000000000..57405f825 --- /dev/null +++ b/js/plugins/vertexai/tests/context-caching/utils_test.ts @@ -0,0 +1,219 @@ +/** + * Copyright 2024 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 { CachedContent, StartChatParams } from '@google-cloud/vertexai'; +import assert from 'assert'; +import crypto from 'crypto'; +import { GenerateRequest } from 'genkit'; +import { describe, it } from 'node:test'; +import { DEFAULT_TTL } from '../../src/context-caching/constants.js'; +import { CacheConfigDetails } from '../../src/context-caching/types.js'; +import { + calculateTTL, + extractCacheConfig, + generateCacheKey, + getContentForCache, + lookupContextCache, +} from '../../src/context-caching/utils.js'; + +describe('generateCacheKey', () => { + it('should generate a SHA-256 hash for a given request object', () => { + const request: CachedContent = { + contents: [ + { + role: 'user', + parts: [{ text: 'Hello world' }], + }, + ], + }; + const expectedHash = crypto + .createHash('sha256') + .update(JSON.stringify(request)) + .digest('hex'); + + const result = generateCacheKey(request); + assert.strictEqual( + result, + expectedHash, + 'Generated hash does not match expected hash' + ); + }); + + it('should handle an empty `contents` array as input', () => { + const request: CachedContent = { + contents: [], + }; + const expectedHash = crypto + .createHash('sha256') + .update(JSON.stringify(request)) + .digest('hex'); + + const result = generateCacheKey(request); + assert.strictEqual( + result, + expectedHash, + 'Generated hash does not match expected hash for empty `contents` array' + ); + }); + + it('should generate different hashes for different objects', () => { + const request1: CachedContent = { + contents: [ + { + role: 'user', + parts: [{ text: 'First message' }], + }, + ], + }; + const request2: CachedContent = { + contents: [ + { + role: 'user', + parts: [{ text: 'Second message' }], + }, + ], + }; + + const hash1 = generateCacheKey(request1); + const hash2 = generateCacheKey(request2); + + assert.notStrictEqual( + hash1, + hash2, + 'Hashes for different objects should not match' + ); + }); + + it('should be consistent for the same input', () => { + const request: CachedContent = { + contents: [ + { + role: 'user', + parts: [{ text: 'Consistent message' }], + }, + ], + }; + const hash1 = generateCacheKey(request); + const hash2 = generateCacheKey(request); + assert.strictEqual(hash1, hash2, 'Hashes for the same object should match'); + }); +}); + +describe('getContentForCache', () => { + it('should correctly retrieve cached content and updated chat request', () => { + const request: GenerateRequest = { + messages: [ + { role: 'system', content: [{ text: 'System message' }] }, + { role: 'user', content: [{ text: 'Hello!' }] }, + ], + }; + const chatRequest: StartChatParams = { + history: [{ role: 'system', parts: [{ text: 'System message' }] }], + }; + const modelVersion = 'vertexai-gpt'; + const cacheConfigDetails: CacheConfigDetails = { + endOfCachedContents: 0, + cacheConfig: { ttlSeconds: 300 }, + }; + + const result = getContentForCache( + request, + chatRequest, + modelVersion, + cacheConfigDetails + ); + + assert.deepStrictEqual(result.cachedContent, { + model: modelVersion, + contents: [{ role: 'system', parts: [{ text: 'System message' }] }], + }); + assert.deepStrictEqual(result.chatRequest.history, []); + assert.deepStrictEqual(result.cacheConfig, { ttlSeconds: 300 }); + }); +}); + +describe('lookupContextCache', () => { + it('should return the cached content if found', async () => { + const mockCacheManager = { + list: async (pageSize: number, pageToken?: string) => ({ + cachedContents: [ + { displayName: 'key1', data: 'value1' }, + { displayName: 'key2', data: 'value2' }, + ], + nextPageToken: undefined, + }), + }; + + const result = await lookupContextCache(mockCacheManager as any, 'key1'); + assert.deepStrictEqual(result, { displayName: 'key1', data: 'value1' }); + }); + + it('should return null if the cached content is not found', async () => { + const mockCacheManager = { + list: async (pageSize: number, pageToken?: string) => ({ + cachedContents: [{ displayName: 'key3', data: 'value3' }], + nextPageToken: undefined, + }), + }; + + const result = await lookupContextCache(mockCacheManager as any, 'key1'); + assert.strictEqual(result, null); + }); +}); + +describe('extractCacheConfig', () => { + it('should correctly extract cache config when metadata.cache is present', () => { + const request: GenerateRequest = { + messages: [ + { + metadata: { cache: { ttlSeconds: 300 } }, + role: 'user', + content: [{ text: 'Hello!' }], + }, + { metadata: {}, role: 'model', content: [{ text: 'Response' }] }, + ], + }; + const result = extractCacheConfig(request); + assert.deepStrictEqual(result, { + endOfCachedContents: 0, + cacheConfig: { ttlSeconds: 300 }, + }); + }); + + it('should handle invalid metadata.cache structures gracefully', () => { + const request: GenerateRequest = { + messages: [ + { + metadata: { cache: 'invalid' }, + role: 'user', + content: [{ text: 'Hello!' }], + }, + ], + }; + assert.throws(() => extractCacheConfig(request)); + }); +}); + +describe('calculateTTL', () => { + it('should return the default TTL when cacheConfig is true', () => { + const cacheConfigDetails: CacheConfigDetails = { + cacheConfig: true, + endOfCachedContents: 0, + }; + const result = calculateTTL(cacheConfigDetails); + assert.strictEqual(result, DEFAULT_TTL); + }); +}); diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index c243ed92e..1dee877f4 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -715,8 +715,8 @@ importers: specifier: ^3.23.0 version: 3.25.0(encoding@0.1.13) '@google-cloud/vertexai': - specifier: ^1.9.0 - version: 1.9.0(encoding@0.1.13) + specifier: ^1.9.2 + version: 1.9.2(encoding@0.1.13) '@mistralai/mistralai-gcp': specifier: ^1.3.5 version: 1.3.5(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.23.8) @@ -746,6 +746,9 @@ importers: '@types/node': specifier: ^20.11.16 version: 20.11.30 + google-gax: + specifier: ^4.4.1 + version: 4.4.1(encoding@0.1.13) npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -787,6 +790,47 @@ importers: specifier: ^5.6.2 version: 5.6.2 + testapps/context-caching: + dependencies: + '@genkit-ai/googleai': + specifier: workspace:* + version: link:../../plugins/googleai + '@genkit-ai/vertexai': + specifier: workspace:* + version: link:../../plugins/vertexai + '@google/generative-ai': + specifier: ^0.21.0 + version: 0.21.0 + genkit: + specifier: workspace:* + version: link:../../genkit + devDependencies: + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + typescript: + specifier: ^5.6.2 + version: 5.6.3 + + testapps/context-caching2: + dependencies: + '@genkit-ai/googleai': + specifier: workspace:* + version: link:../../plugins/googleai + '@google/generative-ai': + specifier: ^0.21.0 + version: 0.21.0 + genkit: + specifier: workspace:* + version: link:../../genkit + devDependencies: + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + typescript: + specifier: ^5.6.2 + version: 5.6.3 + testapps/custom-evaluators: dependencies: '@genkit-ai/googleai': @@ -1248,7 +1292,7 @@ importers: version: link:../../plugins/ollama genkitx-openai: specifier: ^0.10.1 - version: 0.10.1(@genkit-ai/ai@0.9.6)(@genkit-ai/core@0.9.6) + version: 0.10.1(@genkit-ai/ai@0.9.7)(@genkit-ai/core@0.9.7) devDependencies: rimraf: specifier: ^6.0.1 @@ -1357,7 +1401,7 @@ importers: version: link:../../plugins/vertexai '@mistralai/mistralai-gcp': specifier: ^1.3.4 - version: 1.3.4(zod@3.22.4) + version: 1.3.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.22.4) express: specifier: ^4.21.0 version: 4.21.0 @@ -2062,11 +2106,11 @@ packages: '@firebase/util@1.9.5': resolution: {integrity: sha512-PP4pAFISDxsf70l3pEy34Mf3GkkUcVQ3MdKp6aSVb7tcpfUQxnsdV7twDd8EkfB6zZylH6wpUAoangQDmCUMqw==} - '@genkit-ai/ai@0.9.6': - resolution: {integrity: sha512-GiNK8LmCIs/sU6TgVG6GxRpYcIerJdXE649RJsjJQb5pTajOGUYTaVB14nWT4CBKsW2UseSembe0oG64gmxCDg==} + '@genkit-ai/ai@0.9.7': + resolution: {integrity: sha512-CHnN12+J/577EN3uo7qw8tSTa124qwIE+NPHn+TBEtoEkHc70Ck+CxJm0UCfN606hAQyJjj0i6BJ6M4Lr0sMzw==} - '@genkit-ai/core@0.9.6': - resolution: {integrity: sha512-VzcNkVNFSTYNI39bQPAN11QbtZNHuCVxUPcAR0pGAcY6SpqjRSa21WxdLn3dDNjP6sv+pOZdJX3VFln2q5krvg==} + '@genkit-ai/core@0.9.7': + resolution: {integrity: sha512-dNKw172HSzgjgRwf8gwyyjYGnopYwfW3iVPHUaCvIXTCX+C/7kJGYea4Xes7b9ushWYOmhG34q/uea7rhLS/Qg==} '@google-cloud/aiplatform@3.25.0': resolution: {integrity: sha512-qKnJgbyCENjed8e1G5zZGFTxxNKhhaKQN414W2KIVHrLxMFmlMuG+3QkXPOWwXBnT5zZ7aMxypt5og0jCirpHg==} @@ -2146,8 +2190,8 @@ packages: resolution: {integrity: sha512-sZW14pfxEQZSIbBPs6doFYtcbK31Bs3E4jH5Ly3jJnBkYfkMPX8sXG3ZQXCJa88MKtUNPlgBdMN2OJUzmFe5/g==} engines: {node: '>=14'} - '@google-cloud/vertexai@1.9.0': - resolution: {integrity: sha512-8brlcJwFXI4fPuBtsDNQqCdWZmz8gV9jeEKOU0vc5H2SjehCQpXK/NwuSEr916zbhlBHtg/sU37qQQdgvh5BRA==} + '@google-cloud/vertexai@1.9.2': + resolution: {integrity: sha512-pJSUG3r5QIvCFNfkz7/y7kEqvEJaVAk0jZbZoKbcPCRUnXaUeAq7p8I0oklqetGyxbUcZ2FOGpt+Y+4uIltVPg==} engines: {node: '>=18.0.0'} '@google/generative-ai@0.15.0': @@ -2572,11 +2616,6 @@ packages: '@material/material-color-utilities@0.2.7': resolution: {integrity: sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==} - '@mistralai/mistralai-gcp@1.3.4': - resolution: {integrity: sha512-tMWTA8r+MX+/hDn3n1mo8WY/NWN4AJdvwShdgsThrt+bwfwhSlorrz7bH04UFxob13GJ/XsRC3iUg7EAWH0BoQ==} - peerDependencies: - zod: '>= 3' - '@mistralai/mistralai-gcp@1.3.5': resolution: {integrity: sha512-eykxLojLv0AcgGui2D+D/S98Toc18j9g0GCvjyah3E8YtQW0dMb6UyQjmB+2+qDXN3OZjp8+dOkoJ+r7DmwbOQ==} peerDependencies: @@ -4300,6 +4339,10 @@ packages: resolution: {integrity: sha512-3bnD8RASQyaxOYTdWLgwpQco/aytTxFavoI/UN5QN5txDLp8QRrBHNtCUJ5+Ago+551GD92jG8jJduwvmaneUw==} engines: {node: '>=14'} + google-gax@4.4.1: + resolution: {integrity: sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==} + engines: {node: '>=14'} + google-p12-pem@4.0.1: resolution: {integrity: sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==} engines: {node: '>=12.0.0'} @@ -6839,9 +6882,9 @@ snapshots: dependencies: tslib: 2.6.2 - '@genkit-ai/ai@0.9.6': + '@genkit-ai/ai@0.9.7': dependencies: - '@genkit-ai/core': 0.9.6 + '@genkit-ai/core': 0.9.7 '@opentelemetry/api': 1.9.0 '@types/node': 20.16.9 colorette: 2.0.20 @@ -6852,7 +6895,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@genkit-ai/core@0.9.6': + '@genkit-ai/core@0.9.7': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) @@ -7041,7 +7084,7 @@ snapshots: - supports-color optional: true - '@google-cloud/vertexai@1.9.0(encoding@0.1.13)': + '@google-cloud/vertexai@1.9.2(encoding@0.1.13)': dependencies: google-auth-library: 9.14.2(encoding@0.1.13) transitivePeerDependencies: @@ -7327,20 +7370,22 @@ snapshots: '@material/material-color-utilities@0.2.7': {} - '@mistralai/mistralai-gcp@1.3.4(zod@3.22.4)': + '@mistralai/mistralai-gcp@1.3.5(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.23.8)': dependencies: google-auth-library: 9.14.2(encoding@0.1.13) - zod: 3.22.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zod: 3.23.8 transitivePeerDependencies: - encoding - supports-color - '@mistralai/mistralai-gcp@1.3.5(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.23.8)': + '@mistralai/mistralai-gcp@1.3.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.22.4)': dependencies: google-auth-library: 9.14.2(encoding@0.1.13) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - zod: 3.23.8 + zod: 3.22.4 transitivePeerDependencies: - encoding - supports-color @@ -9331,10 +9376,10 @@ snapshots: - encoding - supports-color - genkitx-openai@0.10.1(@genkit-ai/ai@0.9.6)(@genkit-ai/core@0.9.6): + genkitx-openai@0.10.1(@genkit-ai/ai@0.9.7)(@genkit-ai/core@0.9.7): dependencies: - '@genkit-ai/ai': 0.9.6 - '@genkit-ai/core': 0.9.6 + '@genkit-ai/ai': 0.9.7 + '@genkit-ai/core': 0.9.7 openai: 4.53.0(encoding@0.1.13) zod: 3.23.8 transitivePeerDependencies: @@ -9497,6 +9542,24 @@ snapshots: - encoding - supports-color + google-gax@4.4.1(encoding@0.1.13): + dependencies: + '@grpc/grpc-js': 1.10.10 + '@grpc/proto-loader': 0.7.13 + '@types/long': 4.0.2 + abort-controller: 3.0.0 + duplexify: 4.1.3 + google-auth-library: 9.14.2(encoding@0.1.13) + node-fetch: 2.7.0(encoding@0.1.13) + object-hash: 3.0.0 + proto3-json-serializer: 2.0.2 + protobufjs: 7.3.2 + retry-request: 7.0.2(encoding@0.1.13) + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + google-p12-pem@4.0.1: dependencies: node-forge: 1.3.1 diff --git a/js/testapps/context-caching/.gitignore b/js/testapps/context-caching/.gitignore new file mode 100644 index 000000000..6320cd248 --- /dev/null +++ b/js/testapps/context-caching/.gitignore @@ -0,0 +1 @@ +data \ No newline at end of file diff --git a/js/testapps/context-caching/README.md b/js/testapps/context-caching/README.md new file mode 100644 index 000000000..6fa3ffe68 --- /dev/null +++ b/js/testapps/context-caching/README.md @@ -0,0 +1,103 @@ +# Context Caching Test Application + +This is a sample application demonstrating the use of **context caching** with Google's Gemini models via the Google Generative AI plugin in Genkit. The application highlights how to efficiently reuse context in large language model interactions. + +## Features + +- **Context Caching**: Efficiently reuse cached content to improve performance and reduce costs. +- **Supports Large Texts**: Works seamlessly with large texts, such as _War and Peace_ and _Lord of the Rings_. +- **Flexible Model Configuration**: Easily switch between supported Gemini models. + +## Installation + +To run this application, ensure you have the necessary dependencies installed. + +### Prerequisites + +- **Node.js** version 20 or higher +- **npm** or **pnpm** for package management + +### Install Dependencies + +```bash +npm install +``` + +or + +```bash +pnpm install +``` + +## Scripts + +The following scripts are available: + +- **`build`**: Compile TypeScript files. +- **`build:watch`**: Watch for changes and recompile TypeScript files. +- **`start`**: Run the compiled application. +- **`dev`**: Start the application in development mode with environment variable setup. +- **`genkit:dev`**: Run the application with `GENKIT_ENV=dev`. + +## Configuration + +This application uses the Google Generative AI plugin for Genkit. Ensure you have a valid API key for the Gemini API. + +### API Key Setup + +You can configure the API key in two ways: + +1. **Environment Variable**: + + ```bash + export GOOGLE_GENAI_API_KEY=your_api_key_here + ``` + +2. **Inline Configuration** (for testing only, not recommended for production): + ```ts + googleAI({ apiKey: yourKey }); + ``` + +## Usage + +### Running the Application + +To start the application: + +```bash +npm run dev +``` + +or + +```bash +pnpm dev +``` + +### Sample Flow + +The application defines a `lotrFlow` for analyzing _Lord of the Rings_ text. Modify the input file path in the `lotrFlow` definition to test different texts: + +```ts +const defaultQuery = + "Describe Gandalf's relationship with Frodo, referencing Gandalf quotes from the text."; +``` + +### Output + +The output will be generated by the model based on the provided context and query. + +## Dependencies + +- **@genkit-ai/googleai**: Provides the interface for Google Gemini models. +- **genkit**: Framework for integrating generative models. +- **typescript**: TypeScript support for the application. +- **cross-env**: Cross-platform environment variable setup. + +## License + +This project is licensed under the ISC License. + +--- + +**Note**: Ensure your API key is managed securely in production environments. Avoid embedding sensitive information directly in your codebase. diff --git a/js/testapps/context-caching/package.json b/js/testapps/context-caching/package.json new file mode 100644 index 000000000..61c564ed2 --- /dev/null +++ b/js/testapps/context-caching/package.json @@ -0,0 +1,27 @@ +{ + "name": "context-cashing", + "version": "1.0.0", + "description": "", + "main": "lib/index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node lib/index.js", + "build": "tsc", + "build:watch": "tsc --watch", + "genkit:dev": "cross-env GENKIT_ENV=dev npm run dev", + "dev": "export GENKIT_RUNTIME_ID=$(openssl rand -hex 8) && node lib/index.js 2>&1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@genkit-ai/googleai": "workspace:*", + "@genkit-ai/vertexai": "workspace:*", + "@google/generative-ai": "^0.21.0", + "genkit": "workspace:*" + }, + "devDependencies": { + "cross-env": "^7.0.3", + "typescript": "^5.6.2" + } +} diff --git a/js/testapps/context-caching/src/index.ts b/js/testapps/context-caching/src/index.ts new file mode 100644 index 000000000..af5ab6f6f --- /dev/null +++ b/js/testapps/context-caching/src/index.ts @@ -0,0 +1,122 @@ +/** + * Copyright 2024 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 { + gemini15Flash as gemini15FlashGoogleAI, + googleAI, +} from '@genkit-ai/googleai'; +import { gemini15Flash, vertexAI } from '@genkit-ai/vertexai'; // Import specific AI plugins/models +import * as fs from 'fs/promises'; // Import fs module to handle file operations asynchronously +import { genkit, z } from 'genkit'; // Import Genkit framework and Zod for schema validation +import { logger } from 'genkit/logging'; // Import logging utility from Genkit + +const ai = genkit({ + plugins: [vertexAI(), googleAI()], // Initialize Genkit with the Google AI plugin +}); + +logger.setLogLevel('debug'); // Set the logging level to debug for detailed output + +export const lotrFlowVertex = ai.defineFlow( + { + name: 'lotrFlowVertex', // Define a unique name for this flow + inputSchema: z.object({ + query: z.string().optional(), // Define a query input, which is optional + textFilePath: z.string(), // Add the file path to input schema + }), + outputSchema: z.string(), // Define the expected output as a string + }, + async ({ query, textFilePath }) => { + const defaultQuery = 'What is the text i provided you with?'; // Default query to use if none is provided + + // Read the content from the file if the path is provided + const textContent = await fs.readFile(textFilePath, 'utf-8'); // Read the file as UTF-8 encoded text + + const llmResponse = await ai.generate({ + messages: [ + { + role: 'user', // Represents the user's input or query + content: [{ text: textContent }], // Use the loaded file content here + }, + { + role: 'model', // Represents the model's response + content: [ + { + text: 'This is the first few chapters of Lord of the Rings. Can I help in any way?', // Example model response + }, + ], + metadata: { + cache: { + ttlSeconds: 300, // Set the cache time-to-live for this message to 300 seconds + }, // this message is the last one to be cached. + }, + }, + ], + config: { + version: 'gemini-1.5-flash-001', // Specify the version of the model to be used + }, + model: gemini15Flash, // Specify the model (gemini15Flash) to use for generation + prompt: query || defaultQuery, // Use the provided query or fall back to the default query + }); + + return llmResponse.text; // Return the generated text from the model + } +); + +export const lotrFlowGoogleAI = ai.defineFlow( + { + name: 'lotrFlowGoogleAI', // Define a unique name for this flow + inputSchema: z.object({ + query: z.string().optional(), // Define a query input, which is optional + textFilePath: z.string(), // Add the file path to input schema + }), + outputSchema: z.string(), // Define the expected output as a string + }, + async ({ query, textFilePath }) => { + const defaultQuery = 'What is the text i provided you with?'; // Default query to use if none is provided + + // Read the content from the file if the path is provided + const textContent = await fs.readFile(textFilePath, 'utf-8'); // Read the file as UTF-8 encoded text + + const llmResponse = await ai.generate({ + messages: [ + { + role: 'user', // Represents the user's input or query + content: [{ text: textContent }], // Use the loaded file content here + }, + { + role: 'model', // Represents the model's response + content: [ + { + text: 'This is the first few chapters of Lord of the Rings. Can I help in any way?', // Example model response + }, + ], + metadata: { + cache: { + ttlSeconds: 300, // Set the cache time-to-live for this message to 300 seconds + }, // this message is the last one to be cached. + }, + }, + ], + config: { + version: 'gemini-1.5-flash-001', // Specify the version of the model to be used + }, + model: gemini15FlashGoogleAI, // Specify the model (gemini15Flash) to use for generation + prompt: query || defaultQuery, // Use the provided query or fall back to the default query + }); + + return llmResponse.text; // Return the generated text from the model + } +); diff --git a/js/testapps/context-caching/tsconfig.json b/js/testapps/context-caching/tsconfig.json new file mode 100644 index 000000000..efbb566bf --- /dev/null +++ b/js/testapps/context-caching/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compileOnSave": true, + "include": ["src"], + "compilerOptions": { + "module": "commonjs", + "noImplicitReturns": true, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017", + "skipLibCheck": true, + "esModuleInterop": true + } +} diff --git a/js/testapps/context-caching2/.gitignore b/js/testapps/context-caching2/.gitignore new file mode 100644 index 000000000..6320cd248 --- /dev/null +++ b/js/testapps/context-caching2/.gitignore @@ -0,0 +1 @@ +data \ No newline at end of file diff --git a/js/testapps/context-caching2/README.md b/js/testapps/context-caching2/README.md new file mode 100644 index 000000000..19363038d --- /dev/null +++ b/js/testapps/context-caching2/README.md @@ -0,0 +1,105 @@ +# War and Peace Context Caching Test Application + +This sample application demonstrates the use of **context caching** with Google's Gemini models via the Google Generative AI plugin in Genkit. The focus is on analyzing content from large texts like _War and Peace_. + +## Features + +- **Dynamic Content Loading**: Supports loading text from files or fetching default content online. +- **Context Caching**: Reuses previously cached content to optimize performance. +- **Flexible Query Handling**: Allows default and user-defined queries. + +## Installation + +Ensure you have the necessary dependencies installed. + +### Prerequisites + +- **Node.js** version 20 or higher +- **npm** or **pnpm** for package management + +### Install Dependencies + +```bash +npm install +``` + +or + +```bash +pnpm install +``` + +## Scripts + +The following scripts are available: + +- **`build`**: Compile TypeScript files. +- **`build:watch`**: Watch for changes and recompile TypeScript files. +- **`start`**: Run the compiled application. +- **`dev`**: Start the application in development mode with environment variable setup. +- **`genkit:dev`**: Run the application with `GENKIT_ENV=dev`. + +## Configuration + +This application uses the Google Generative AI plugin for Genkit. Ensure you have a valid API key for the Gemini API. + +### API Key Setup + +You can configure the API key in two ways: + +1. **Environment Variable**: + + ```bash + export GOOGLE_GENAI_API_KEY=your_api_key_here + ``` + +2. **Inline Configuration** (for testing only, not recommended for production): + ```ts + googleAI({ apiKey: yourKey }); + ``` + +## Usage + +### Running the Application + +To start the application: + +```bash +npm run dev +``` + +or + +```bash +pnpm dev +``` + +### Sample Flow + +The application defines a `warAndPeaceFlow` for analyzing _War and Peace_. You can either provide a local text file path or use the default content fetched online. + +Example: + +```ts +const defaultQuery = + "Describe Pierre Bezukhov's transformation throughout the novel."; +``` + +### Output + +The output will be generated by the model based on the provided context and query. + +## Dependencies + +- **@genkit-ai/googleai**: Provides the interface for Google Gemini models. +- **genkit**: Framework for integrating generative models. +- **typescript**: TypeScript support for the application. +- **cross-env**: Cross-platform environment variable setup. + +## License + +This project is licensed under the ISC License. + +--- + +**Note**: Ensure your API key is managed securely in production environments. Avoid embedding sensitive information directly in your codebase. diff --git a/js/testapps/context-caching2/package.json b/js/testapps/context-caching2/package.json new file mode 100644 index 000000000..d827a07ff --- /dev/null +++ b/js/testapps/context-caching2/package.json @@ -0,0 +1,26 @@ +{ + "name": "context-cashing2", + "version": "1.0.0", + "description": "", + "main": "lib/index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node lib/index.js", + "build": "tsc", + "build:watch": "tsc --watch", + "genkit:dev": "cross-env GENKIT_ENV=dev npm run dev", + "dev": "export GENKIT_RUNTIME_ID=$(openssl rand -hex 8) && node lib/index.js 2>&1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@genkit-ai/googleai": "workspace:*", + "@google/generative-ai": "^0.21.0", + "genkit": "workspace:*" + }, + "devDependencies": { + "cross-env": "^7.0.3", + "typescript": "^5.6.2" + } +} diff --git a/js/testapps/context-caching2/src/index.ts b/js/testapps/context-caching2/src/index.ts new file mode 100644 index 000000000..837c97477 --- /dev/null +++ b/js/testapps/context-caching2/src/index.ts @@ -0,0 +1,97 @@ +/** + * Copyright 2024 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 { gemini15Flash, googleAI } from '@genkit-ai/googleai'; // Import specific plugins/models for generative AI +import * as fs from 'fs/promises'; // Import file system promises for reading files +import { genkit, z } from 'genkit'; // Import Genkit framework and Zod schema validation +import { logger } from 'genkit/logging'; // Import logger for debugging and logging + +// If using Node.js <18: +// import fetch from 'node-fetch'; // Install this if `fetch` is not natively available. + +const ai = genkit({ + plugins: [googleAI()], // Initialize Genkit with Google AI plugin +}); + +logger.setLogLevel('debug'); // Set logging level to debug for detailed logs + +export const warAndPeaceFlow = ai.defineFlow( + { + name: 'warAndPeaceFlow', // Define a unique name for this flow + inputSchema: z.object({ + query: z.string().optional(), // Define user input schema for query, optional string + textFilePath: z.string().optional(), // Define schema for the text file path, optional string + }), + outputSchema: z.string(), // Define the expected output schema, a string + }, + async ({ query, textFilePath }) => { + const defaultQuery = + "Describe Pierre Bezukhov's transformation throughout the novel."; // Default query if the user doesn't provide one + + let textContent; + + if (textFilePath) { + // Read the content from the provided file path + textContent = await fs.readFile(textFilePath, 'utf-8'); + } else { + // Fetch the default content from the provided link + const response = await fetch( + 'https://www.gutenberg.org/cache/epub/2600/pg2600.txt' + ); + if (!response.ok) { + throw new Error('Failed to fetch default text content'); + } + textContent = await response.text(); + } + + // Generate a response using AI with the following parameters + const llmResponse = await ai.generate({ + messages: [ + { + role: 'user', // User message providing context or query + content: [{ text: textContent }], // Include the loaded text content as an array of parts + }, + { + role: 'model', // Model's response to the user's query + content: [ + { + text: 'Here is some analysis based on the text provided.', + }, + ], + metadata: { + cache: { + ttlSeconds: 300, // Cache TTL for this specific response + }, + }, + }, + ], + config: { + version: 'gemini-1.5-flash-001', // Specify the model version + temperature: 0.7, // Control randomness in the output + maxOutputTokens: 1000, // Limit the maximum number of tokens for the response + topK: 50, // Limit token selection to top K probabilities + topP: 0.9, // Apply nucleus sampling with 0.9 threshold + stopSequences: ['END'], // Define custom sequences to stop the generation + }, + tools: [], // No tools used in this request + model: gemini15Flash, // Specify the generative model to use + prompt: query || defaultQuery, // Use user's query or default query for the main task + returnToolRequests: false, // Prevent tool requests from being returned automatically + }); + + return llmResponse.text; // Return the generated response text + } +); diff --git a/js/testapps/context-caching2/tsconfig.json b/js/testapps/context-caching2/tsconfig.json new file mode 100644 index 000000000..efbb566bf --- /dev/null +++ b/js/testapps/context-caching2/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compileOnSave": true, + "include": ["src"], + "compilerOptions": { + "module": "commonjs", + "noImplicitReturns": true, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017", + "skipLibCheck": true, + "esModuleInterop": true + } +} From 4c282a5464f57c3c3fa61c32064278f6518ef7d3 Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Fri, 6 Dec 2024 17:30:11 -0500 Subject: [PATCH 034/562] include actionConfig in eval metadata (#1464) --- genkit-tools/common/src/eval/evaluate.ts | 7 ++++++- genkit-tools/common/src/types/eval.ts | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/genkit-tools/common/src/eval/evaluate.ts b/genkit-tools/common/src/eval/evaluate.ts index 0b4ea4266..b02ad8580 100644 --- a/genkit-tools/common/src/eval/evaluate.ts +++ b/genkit-tools/common/src/eval/evaluate.ts @@ -93,7 +93,12 @@ export async function runNewEvaluation( manager, evaluatorActions, evalDataset, - augments: { actionRef, datasetId, datasetVersion }, + augments: { + actionRef, + datasetId, + datasetVersion, + actionConfig: request.options?.actionConfig, + }, }); return evalRun.key; } diff --git a/genkit-tools/common/src/types/eval.ts b/genkit-tools/common/src/types/eval.ts index dbe9b0914..120769f97 100644 --- a/genkit-tools/common/src/types/eval.ts +++ b/genkit-tools/common/src/types/eval.ts @@ -129,12 +129,14 @@ export const EvalRunKeySchema = z.object({ datasetVersion: z.number().optional(), evalRunId: z.string(), createdAt: z.string(), + actionConfig: z.any().optional(), }); export type EvalRunKey = z.infer; export const EvalKeyAugmentsSchema = EvalRunKeySchema.pick({ datasetId: true, datasetVersion: true, actionRef: true, + actionConfig: true, }); export type EvalKeyAugments = z.infer; From fa11e2e5e1c0b65a4c964d41a6cb881b0758ba63 Mon Sep 17 00:00:00 2001 From: Jia Hao Date: Tue, 10 Dec 2024 22:06:49 +0800 Subject: [PATCH 035/562] Fix googleai typo (#1479) --- docs/evaluation.md | 2 +- docs/templates/pgvector.md | 2 +- docs/tool-calling.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/evaluation.md b/docs/evaluation.md index d00dcd930..b27918265 100644 --- a/docs/evaluation.md +++ b/docs/evaluation.md @@ -34,7 +34,7 @@ const ai = genkit({ ``` **Note:** The configuration above requires installing the `genkit`, -`@genkit-ai/google-ai`, `@genkit-ai/evaluator` and `@genkit-ai/vertexai` +`@genkit-ai/googleai`, `@genkit-ai/evaluator` and `@genkit-ai/vertexai` packages. ```posix-terminal diff --git a/docs/templates/pgvector.md b/docs/templates/pgvector.md index ebc82d3ec..52fa35868 100644 --- a/docs/templates/pgvector.md +++ b/docs/templates/pgvector.md @@ -6,7 +6,7 @@ schema. ```ts import { genkit, z } from 'genkit'; -import { googleAI, textEmbedding004 } from '@genkit-ai/google-ai'; +import { googleAI, textEmbedding004 } from '@genkit-ai/googleai'; import { toSql } from 'pgvector'; import postgres from 'postgres'; diff --git a/docs/tool-calling.md b/docs/tool-calling.md index ab747b587..280e030e1 100644 --- a/docs/tool-calling.md +++ b/docs/tool-calling.md @@ -96,7 +96,7 @@ Use the Genkit instance's `defineTool()` function to write tool definitions: ```ts import { genkit, z } from 'genkit'; -import { googleAI, gemini15Flash } from '@genkit-ai/google-ai'; +import { googleAI, gemini15Flash } from '@genkitai/google-ai'; const ai = genkit({ plugins: [googleAI()], From cab3bd283c879811290d4215f4764f5e5ef0b554 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Tue, 10 Dec 2024 10:02:54 -0500 Subject: [PATCH 036/562] chore(js/genkit): added a few more prompt tests (#1468) --- js/genkit/tests/prompts_test.ts | 102 +++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 3 deletions(-) diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index 622cb42fa..d1d58b066 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -270,7 +270,7 @@ describe('definePrompt - dotprompt', () => { assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); }); - it('calls dotprompt with history', async () => { + it('calls dotprompt with history and places it at {{ history }}', async () => { const hi = ai.definePrompt( { name: 'hi', @@ -281,7 +281,7 @@ describe('definePrompt - dotprompt', () => { }), }, }, - '{{ history}} hi {{ name }}' + '{{ role "system"}} talk like a pirate {{ history}} hi {{ name }}' ); const response = await hi( @@ -294,6 +294,10 @@ describe('definePrompt - dotprompt', () => { } ); assert.deepStrictEqual(response.messages, [ + { + role: 'system', + content: [{ text: ' talk like a pirate ' }], + }, { role: 'user', content: [{ text: 'hi' }], @@ -311,7 +315,99 @@ describe('definePrompt - dotprompt', () => { { role: 'model', content: [ - { text: 'Echo: hi,bye, hi Genkit' }, + { text: 'Echo: system: talk like a pirate ,hi,bye, hi Genkit' }, + { text: '; config: {}' }, + ], + }, + ]); + }); + + it('calls dotprompt with history and places it before user message', async () => { + const hi = ai.definePrompt( + { + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), + }, + }, + 'hi {{ name }}' + ); + + const response = await hi( + { name: 'Genkit' }, + { + messages: [ + { role: 'user', content: [{ text: 'hi' }] }, + { role: 'model', content: [{ text: 'bye' }] }, + ], + } + ); + assert.deepStrictEqual(response.messages, [ + { + role: 'user', + content: [{ text: 'hi' }], + }, + { + role: 'model', + content: [{ text: 'bye' }], + }, + { + role: 'user', + content: [{ text: 'hi Genkit' }], + }, + { + role: 'model', + content: [ + { text: 'Echo: hi,bye,hi Genkit' }, + { text: '; config: {}' }, + ], + }, + ]); + }); + + it('streams dotprompt with history and places it before user message', async () => { + const hi = ai.definePrompt( + { + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), + }, + }, + 'hi {{ name }}' + ); + + const response = await hi.stream( + { name: 'Genkit' }, + { + messages: [ + { role: 'user', content: [{ text: 'hi' }] }, + { role: 'model', content: [{ text: 'bye' }] }, + ], + } + ); + assert.deepStrictEqual((await response.response).messages, [ + { + role: 'user', + content: [{ text: 'hi' }], + }, + { + role: 'model', + content: [{ text: 'bye' }], + }, + { + role: 'user', + content: [{ text: 'hi Genkit' }], + }, + { + role: 'model', + content: [ + { text: 'Echo: hi,bye,hi Genkit' }, { text: '; config: {}' }, ], }, From ec0c95db88084092db6b6e3473b10b24024ce257 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Tue, 10 Dec 2024 11:07:27 -0500 Subject: [PATCH 037/562] feat(js/plugins/express): added new express plugin for various express integrations (#1434) --- js/core/src/flow.ts | 33 +- js/genkit/src/client/client.ts | 24 +- js/genkit/src/genkit.ts | 6 +- js/plugins/express/README.md | 93 ++++++ js/plugins/express/package.json | 54 ++++ js/plugins/express/src/index.ts | 176 +++++++++++ js/plugins/express/src/utils.ts | 35 +++ js/plugins/express/tests/express_test.ts | 377 +++++++++++++++++++++++ js/plugins/express/tsconfig.json | 4 + js/plugins/express/tsup.config.ts | 22 ++ js/plugins/express/typedoc.json | 3 + js/pnpm-lock.yaml | 85 ++++- js/testapps/express/package.json | 1 + js/testapps/express/src/index.ts | 64 +++- 14 files changed, 948 insertions(+), 29 deletions(-) create mode 100644 js/plugins/express/README.md create mode 100644 js/plugins/express/package.json create mode 100644 js/plugins/express/src/index.ts create mode 100644 js/plugins/express/src/utils.ts create mode 100644 js/plugins/express/tests/express_test.ts create mode 100644 js/plugins/express/tsconfig.json create mode 100644 js/plugins/express/tsup.config.ts create mode 100644 js/plugins/express/typedoc.json diff --git a/js/core/src/flow.ts b/js/core/src/flow.ts index 2dd53d802..9fac492c6 100644 --- a/js/core/src/flow.ts +++ b/js/core/src/flow.ts @@ -81,10 +81,11 @@ export interface StreamingFlowConfig< streamSchema?: S; } -export interface FlowCallOptions { +export interface FlowCallOptions { /** @deprecated use {@link context} instead. */ withLocalAuthContext?: unknown; context?: unknown; + onChunk?: StreamingCallback; } /** @@ -95,9 +96,12 @@ export interface CallableFlow< O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny, > { - (input?: z.infer, opts?: FlowCallOptions): Promise>; + (input?: z.infer, opts?: FlowCallOptions>): Promise>; - stream(input?: z.infer, opts?: FlowCallOptions): StreamingResponse; + stream( + input?: z.infer, + opts?: FlowCallOptions> + ): StreamingResponse; flow: Flow; } @@ -111,7 +115,10 @@ export interface StreamableFlow< O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny, > { - (input?: z.infer, opts?: FlowCallOptions): StreamingResponse; + ( + input?: z.infer, + opts?: FlowCallOptions> + ): StreamingResponse; flow: Flow; } @@ -176,7 +183,7 @@ export class Flow< async invoke( input: unknown, opts: { - streamingCallback?: StreamingCallback>; + onChunk?: StreamingCallback>; labels?: Record; context?: unknown; } @@ -185,14 +192,17 @@ export class Flow< return await this.action.run(input, { context: opts.context, telemetryLabels: opts.labels, - onChunk: opts.streamingCallback ?? (() => {}), + onChunk: opts.onChunk ?? (() => {}), }); } /** * Runs the flow. This is used when calling a flow from another flow. */ - async run(payload?: z.infer, opts?: FlowCallOptions): Promise> { + async run( + payload?: z.infer, + opts?: FlowCallOptions> + ): Promise> { const input = this.inputSchema ? this.inputSchema.parse(payload) : payload; await this.authPolicy?.(opts?.withLocalAuthContext, payload); @@ -204,6 +214,7 @@ export class Flow< const result = await this.invoke(input, { context: opts?.context || opts?.withLocalAuthContext, + onChunk: opts?.onChunk, }); return result.result; } @@ -213,7 +224,7 @@ export class Flow< */ stream( payload?: z.infer, - opts?: FlowCallOptions + opts?: Omit>, 'onChunk'> ): StreamingResponse { let chunkStreamController: ReadableStreamController>; const chunkStream = new ReadableStream>({ @@ -233,7 +244,7 @@ export class Flow< this.invoke( this.inputSchema ? this.inputSchema.parse(payload) : payload, { - streamingCallback: ((chunk: z.infer) => { + onChunk: ((chunk: z.infer) => { chunkStreamController.enqueue(chunk); }) as S extends z.ZodVoid ? undefined @@ -293,7 +304,7 @@ export class Flow< }); try { const result = await this.invoke(input, { - streamingCallback: (chunk: z.infer) => { + onChunk: (chunk: z.infer) => { response.write( 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter ); @@ -484,7 +495,7 @@ export function defineFlow< (callableFlow as CallableFlow).flow = flow; (callableFlow as CallableFlow).stream = ( input: z.infer, - opts: FlowCallOptions + opts: Omit>, 'onChunk'> ): StreamingResponse => { return flow.stream(input, opts); }; diff --git a/js/genkit/src/client/client.ts b/js/genkit/src/client/client.ts index 7da762596..9f5bc9e84 100644 --- a/js/genkit/src/client/client.ts +++ b/js/genkit/src/client/client.ts @@ -34,7 +34,7 @@ const __flowStreamDelimiter = '\n\n'; * console.log(await response.output); * ``` */ -export function streamFlow({ +export function streamFlow({ url, input, headers, @@ -42,7 +42,10 @@ export function streamFlow({ url: string; input?: any; headers?: Record; -}) { +}): { + output(): Promise; + stream(): AsyncIterable; +} { let chunkStreamController: ReadableStreamDefaultController | undefined = undefined; const chunkStream = new ReadableStream({ @@ -97,8 +100,7 @@ async function __flowRunEnvelope({ streamingCallback: (chunk: any) => void; headers?: Record; }) { - let response; - response = await fetch(url, { + const response = await fetch(url, { method: 'POST', body: JSON.stringify({ data: input, @@ -109,6 +111,11 @@ async function __flowRunEnvelope({ ...headers, }, }); + if (response.status !== 200) { + throw new Error( + `Server returned: ${response.status}: ${await response.text()}` + ); + } if (!response.body) { throw new Error('Response body is empty'); } @@ -163,7 +170,7 @@ async function __flowRunEnvelope({ * console.log(await response); * ``` */ -export async function runFlow({ +export async function runFlow({ url, input, headers, @@ -171,7 +178,7 @@ export async function runFlow({ url: string; input?: any; headers?: Record; -}) { +}): Promise { const response = await fetch(url, { method: 'POST', body: JSON.stringify({ @@ -182,6 +189,11 @@ export async function runFlow({ ...headers, }, }); + if (response.status !== 200) { + throw new Error( + `Server returned: ${response.status}: ${await response.text()}` + ); + } const wrappedDesult = await response.json(); return wrappedDesult.result; } diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index e091ca222..673ff8fd3 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -180,7 +180,7 @@ export class Genkit { /** Flow server. May be null if the flow server is not enabled in configuration or not started. */ private flowServer: FlowServer | null = null; /** List of flows that have been registered in this instance. */ - private registeredFlows: Flow[] = []; + readonly flows: Flow[] = []; constructor(options?: GenkitOptions) { this.options = options || {}; @@ -208,7 +208,7 @@ export class Genkit { fn: FlowFn ): CallableFlow { const flow = defineFlow(this.registry, config, fn); - this.registeredFlows.push(flow.flow); + this.flows.push(flow.flow); return flow; } @@ -230,7 +230,7 @@ export class Genkit { typeof config === 'string' ? { name: config } : config, fn ); - this.registeredFlows.push(flow.flow); + this.flows.push(flow.flow); return flow; } diff --git a/js/plugins/express/README.md b/js/plugins/express/README.md new file mode 100644 index 000000000..805e3d86d --- /dev/null +++ b/js/plugins/express/README.md @@ -0,0 +1,93 @@ +# Genkit Express Plugin + +This plugin provides utilities for conveninetly exposing Genkit flows and actions via Express HTTP server as REST APIs. + +```ts +import { handler } from '@genkit-ai/express'; +import express from 'express'; + +const simpleFlow = ai.defineFlow( + 'simpleFlow', + async (input, streamingCallback) => { + const { text } = await ai.generate({ + model: gemini15Flash, + prompt: input, + streamingCallback, + }); + return text; + } +); + +const app = express(); +app.use(express.json()); + +app.post('/simpleFlow', handler(simpleFlow)); + +app.listen(8080); +``` + +You can also set auth policies: + +```ts +// middleware for handling auth headers. +const authMiddleware = async (req, resp, next) => { + // parse auth headers and convert to auth object. + (req as RequestWithAuth).auth = { + user: + req.header('authorization') === 'open sesame' ? 'Ali Baba' : '40 thieves', + }; + next(); +}; + +app.post( + '/simpleFlow', + authMiddleware, + handler(simpleFlow, { + authPolicy: ({ auth }) => { + if (auth.user !== 'Ali Baba') { + throw new Error('not authorized'); + } + }, + }) +); +``` + +Flows and actions exposed using the `handler` function can be accessed using `genkit/client` library: + +```ts +import { runFlow, streamFlow } from 'genkit/client'; + +const result = await runFlow({ + url: `http://localhost:${port}/simpleFlow`, + input: 'say hello', +}); + +console.log(result); // hello + +// set auth headers (when using auth policies) +const result = await runFlow({ + url: `http://localhost:${port}/simpleFlow`, + headers: { + Authorization: 'open sesame', + }, + input: 'say hello', +}); + +console.log(result); // hello + +// and streamed +const result = streamFlow({ + url: `http://localhost:${port}/simpleFlow`, + input: 'say hello', +}); +for await (const chunk of result.stream()) { + console.log(chunk); +} +console.log(await result.output()); +``` + +The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. + +Usage information and reference details can be found in [Genkit documentation](https://firebase.google.com/docs/genkit). + +License: Apache 2.0 diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json new file mode 100644 index 000000000..8edd01041 --- /dev/null +++ b/js/plugins/express/package.json @@ -0,0 +1,54 @@ +{ + "name": "@genkit-ai/express", + "description": "Genkit AI framework plugin for Express server", + "keywords": [ + "genkit", + "genkit-plugin", + "langchain", + "ai", + "genai", + "generative-ai" + ], + "version": "0.9.6", + "type": "commonjs", + "scripts": { + "check": "tsc", + "compile": "tsup-node", + "build:clean": "rimraf ./lib", + "build": "npm-run-all build:clean check compile", + "build:watch": "tsup-node --watch", + "test": "node --import tsx --test tests/*_test.ts", + "test:watch": "node --import tsx --watch --test tests/*_test.ts" + }, + "repository": { + "type": "git", + "url": "https://github.com/firebase/genkit.git", + "directory": "js/plugins/express" + }, + "author": "genkit", + "license": "Apache-2.0", + "dependencies": {}, + "peerDependencies": { + "genkit": "workspace:*", + "express": "^4.21.1" + }, + "devDependencies": { + "get-port": "^5.1.0", + "@types/express": "^4.17.21", + "@types/node": "^20.11.16", + "npm-run-all": "^4.1.5", + "rimraf": "^6.0.1", + "tsup": "^8.3.5", + "tsx": "^4.19.2", + "typescript": "^4.9.0" + }, + "types": "./lib/index.d.ts", + "exports": { + ".": { + "require": "./lib/index.js", + "default": "./lib/index.js", + "import": "./lib/index.mjs", + "types": "./lib/index.d.ts" + } + } +} diff --git a/js/plugins/express/src/index.ts b/js/plugins/express/src/index.ts new file mode 100644 index 000000000..9afdf7dca --- /dev/null +++ b/js/plugins/express/src/index.ts @@ -0,0 +1,176 @@ +/** + * Copyright 2024 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 express from 'express'; +import { + Action, + CallableFlow, + Flow, + runWithStreamingCallback, + z, +} from 'genkit'; +import { logger } from 'genkit/logging'; +import { getErrorMessage, getErrorStack } from './utils'; + +const streamDelimiter = '\n\n'; + +/** + * Auth policy context is an object passed to the auth policy providing details necessary for auth. + */ +export interface AuthPolicyContext< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + S extends z.ZodTypeAny = z.ZodTypeAny, +> { + flow?: Flow; + action?: Action; + input: z.infer; + auth: any | undefined; + request: RequestWithAuth; +} + +/** + * Flow Auth policy. Consumes the authorization context of the flow and + * performs checks before the flow runs. If this throws, the flow will not + * be executed. + */ +export interface AuthPolicy< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + S extends z.ZodTypeAny = z.ZodTypeAny, +> { + (ctx: AuthPolicyContext): void | Promise; +} + +/** + * For express-based flows, req.auth should contain the value to bepassed into + * the flow context. + */ +export interface RequestWithAuth extends express.Request { + auth?: unknown; +} + +/** + * Exposes provided flow or an action as express handler. + */ +export function handler< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + S extends z.ZodTypeAny = z.ZodTypeAny, +>( + f: CallableFlow | Flow | Action, + opts?: { + authPolicy?: AuthPolicy; + } +): express.RequestHandler { + const flow: Flow | undefined = (f as Flow).invoke + ? (f as Flow) + : (f as CallableFlow).flow + ? (f as CallableFlow).flow + : undefined; + const action: Action = flow ? flow.action : (f as Action); + return async ( + request: RequestWithAuth, + response: express.Response + ): Promise => { + const { stream } = request.query; + let input = request.body.data; + const auth = request.auth; + + try { + await opts?.authPolicy?.({ + flow, + action, + auth, + input, + request, + }); + } catch (e: any) { + logger.debug(e); + const respBody = { + error: { + status: 'PERMISSION_DENIED', + message: e.message || 'Permission denied to resource', + }, + }; + response.status(403).send(respBody).end(); + return; + } + + if (request.get('Accept') === 'text/event-stream' || stream === 'true') { + response.writeHead(200, { + 'Content-Type': 'text/plain', + 'Transfer-Encoding': 'chunked', + }); + try { + const onChunk = (chunk: z.infer) => { + response.write( + 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter + ); + }; + const result = await runWithStreamingCallback(onChunk, () => + action.run(input, { + onChunk, + context: auth, + }) + ); + response.write( + 'data: ' + JSON.stringify({ result: result.result }) + streamDelimiter + ); + response.end(); + } catch (e) { + logger.error(e); + response.write( + 'data: ' + + JSON.stringify({ + error: { + status: 'INTERNAL', + message: getErrorMessage(e), + details: getErrorStack(e), + }, + }) + + streamDelimiter + ); + response.end(); + } + } else { + try { + const result = await action.run(input, { context: auth }); + response.setHeader('x-genkit-trace-id', result.telemetry.traceId); + response.setHeader('x-genkit-span-id', result.telemetry.spanId); + // Responses for non-streaming flows are passed back with the flow result stored in a field called "result." + response + .status(200) + .send({ + result: result.result, + }) + .end(); + } catch (e) { + // Errors for non-streaming flows are passed back as standard API errors. + response + .status(500) + .send({ + error: { + status: 'INTERNAL', + message: getErrorMessage(e), + details: getErrorStack(e), + }, + }) + .end(); + } + } + }; +} diff --git a/js/plugins/express/src/utils.ts b/js/plugins/express/src/utils.ts new file mode 100644 index 000000000..b7c823b3f --- /dev/null +++ b/js/plugins/express/src/utils.ts @@ -0,0 +1,35 @@ +/** + * Copyright 2024 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. + */ + +/** + * Extracts error message from the given error object, or if input is not an error then just turn the error into a string. + */ +export function getErrorMessage(e: any): string { + if (e instanceof Error) { + return e.message; + } + return `${e}`; +} + +/** + * Extracts stack trace from the given error object, or if input is not an error then returns undefined. + */ +export function getErrorStack(e: any): string | undefined { + if (e instanceof Error) { + return e.stack; + } + return undefined; +} diff --git a/js/plugins/express/tests/express_test.ts b/js/plugins/express/tests/express_test.ts new file mode 100644 index 000000000..e3341c382 --- /dev/null +++ b/js/plugins/express/tests/express_test.ts @@ -0,0 +1,377 @@ +/** + * Copyright 2024 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 express from 'express'; +import { GenerateResponseData, Genkit, genkit, z } from 'genkit'; +import { runFlow, streamFlow } from 'genkit/client'; +import { GenerateResponseChunkData, ModelAction } from 'genkit/model'; +import getPort from 'get-port'; +import * as http from 'http'; +import assert from 'node:assert'; +import { afterEach, beforeEach, describe, it } from 'node:test'; +import { getFlowContext } from '../../../core/lib/auth.js'; +import { RequestWithAuth, handler } from '../src/index.js'; + +describe('telemetry', async () => { + let server: http.Server; + let port; + + beforeEach(async () => { + const ai = genkit({}); + const echoModel = defineEchoModel(ai); + + const voidInput = ai.defineFlow('voidInput', async () => { + return 'banana'; + }); + + const stringInput = ai.defineFlow('stringInput', async (input) => { + const { text } = await ai.generate({ + model: 'echoModel', + prompt: input, + }); + return text; + }); + + const objectInput = ai.defineFlow( + { name: 'objectInput', inputSchema: z.object({ question: z.string() }) }, + async (input) => { + const { text } = await ai.generate({ + model: 'echoModel', + prompt: input.question, + }); + return text; + } + ); + + const streamingFlow = ai.defineFlow( + { + name: 'streamingFlow', + inputSchema: z.object({ question: z.string() }), + }, + async (input, sendChunk) => { + const { text } = await ai.generate({ + model: 'echoModel', + prompt: input.question, + streamingCallback: sendChunk, + }); + return text; + } + ); + + const flowWithContext = ai.defineFlow( + { + name: 'flowWithContext', + inputSchema: z.object({ question: z.string() }), + }, + async (input) => { + return `${input.question} - ${JSON.stringify(getFlowContext())}`; + } + ); + + const app = express(); + app.use(express.json()); + port = await getPort(); + + app.post('/voidInput', handler(voidInput)); + app.post('/stringInput', handler(stringInput)); + app.post('/objectInput', handler(objectInput)); + app.post('/streamingFlow', handler(streamingFlow)); + app.post( + '/flowWithAuth', + async (req, resp, next) => { + (req as RequestWithAuth).auth = { + user: + req.header('authorization') === 'open sesame' + ? 'Ali Baba' + : '40 thieves', + }; + next(); + }, + handler(flowWithContext, { + authPolicy: ({ auth, action, input, request }) => { + assert.ok(auth, 'auth must be set'); + assert.ok(action, 'flow must be set'); + assert.ok(input, 'input must be set'); + assert.ok(request, 'request must be set'); + + if (auth.user !== 'Ali Baba') { + throw new Error('not authorized'); + } + }, + }) + ); + + // Can also expose any action. + app.post('/echoModel', handler(echoModel)); + app.post( + '/echoModelWithAuth', + async (req, resp, next) => { + (req as RequestWithAuth).auth = { + user: + req.header('authorization') === 'open sesame' + ? 'Ali Baba' + : '40 thieves', + }; + next(); + }, + handler(echoModel, { + authPolicy: ({ auth, action, input, request }) => { + assert.ok(auth, 'auth must be set'); + assert.ok(action, 'flow must be set'); + assert.ok(input, 'input must be set'); + assert.ok(request, 'request must be set'); + + if (auth.user !== 'Ali Baba') { + throw new Error('not authorized'); + } + }, + }) + ); + + server = app.listen(port, () => { + console.log(`Example app listening on port ${port}`); + }); + }); + + afterEach(() => { + server.close(); + }); + + describe('runFlow', () => { + it('should call a void input flow', async () => { + const result = await runFlow({ + url: `http://localhost:${port}/voidInput`, + }); + assert.strictEqual(result, 'banana'); + }); + + it('should run a flow with string input', async () => { + const result = await runFlow({ + url: `http://localhost:${port}/stringInput`, + input: 'hello', + }); + assert.strictEqual(result, 'Echo: hello'); + }); + + it('should run a flow with object input', async () => { + const result = await runFlow({ + url: `http://localhost:${port}/objectInput`, + input: { + question: 'olleh', + }, + }); + assert.strictEqual(result, 'Echo: olleh'); + }); + + it('should fail a bad input', async () => { + const result = runFlow({ + url: `http://localhost:${port}/objectInput`, + input: { + badField: 'hello', + }, + }); + await assert.rejects(result, (err: Error) => { + return err.message.includes('INVALID_ARGUMENT'); + }); + }); + + it('should call a flow with auth', async () => { + const result = await runFlow({ + url: `http://localhost:${port}/flowWithAuth`, + input: { + question: 'hello', + }, + headers: { + Authorization: 'open sesame', + }, + }); + assert.strictEqual(result, 'hello - {"user":"Ali Baba"}'); + }); + + it('should fail a flow with auth', async () => { + const result = runFlow({ + url: `http://localhost:${port}/flowWithAuth`, + input: { + question: 'hello', + }, + headers: { + Authorization: 'thieve #24', + }, + }); + await assert.rejects(result, (err) => { + return (err as Error).message.includes('not authorized'); + }); + }); + + it('should call a model', async () => { + const result = await runFlow({ + url: `http://localhost:${port}/echoModel`, + input: { + messages: [{ role: 'user', content: [{ text: 'hello' }] }], + }, + }); + assert.strictEqual(result.finishReason, 'stop'); + assert.deepStrictEqual(result.message, { + role: 'model', + content: [{ text: 'Echo: hello' }], + }); + }); + + it('should call a model with auth', async () => { + const result = await runFlow({ + url: `http://localhost:${port}/echoModelWithAuth`, + input: { + messages: [{ role: 'user', content: [{ text: 'hello' }] }], + }, + headers: { + Authorization: 'open sesame', + }, + }); + assert.strictEqual(result.finishReason, 'stop'); + assert.deepStrictEqual(result.message, { + role: 'model', + content: [{ text: 'Echo: hello' }], + }); + }); + + it('should fail a flow with auth', async () => { + const result = runFlow({ + url: `http://localhost:${port}/echoModelWithAuth`, + input: { + messages: [ + { + role: 'user', + content: [{ text: 'hello' }], + }, + ], + }, + headers: { + Authorization: 'thieve #24', + }, + }); + await assert.rejects(result, (err) => { + return (err as Error).message.includes('not authorized'); + }); + }); + }); + + describe('streamFlow', () => { + it('stream a flow', async () => { + const result = streamFlow({ + url: `http://localhost:${port}/streamingFlow`, + input: { + question: 'olleh', + }, + }); + + const gotChunks: GenerateResponseChunkData[] = []; + for await (const chunk of result.stream()) { + gotChunks.push(chunk); + } + + assert.deepStrictEqual(gotChunks, [ + { content: [{ text: '3' }] }, + { content: [{ text: '2' }] }, + { content: [{ text: '1' }] }, + ]); + + assert.strictEqual(await result.output(), 'Echo: olleh'); + }); + + it('stream a model', async () => { + const result = streamFlow({ + url: `http://localhost:${port}/echoModel`, + input: { + messages: [ + { + role: 'user', + content: [{ text: 'olleh' }], + }, + ], + }, + }); + + const gotChunks: any[] = []; + for await (const chunk of result.stream()) { + gotChunks.push(chunk); + } + + const output = await result.output(); + assert.strictEqual(output.finishReason, 'stop'); + assert.deepStrictEqual(output.message, { + role: 'model', + content: [{ text: 'Echo: olleh' }], + }); + + assert.deepStrictEqual(gotChunks, [ + { content: [{ text: '3' }] }, + { content: [{ text: '2' }] }, + { content: [{ text: '1' }] }, + ]); + }); + }); +}); + +export function defineEchoModel(ai: Genkit): ModelAction { + return ai.defineModel( + { + name: 'echoModel', + }, + async (request, streamingCallback) => { + streamingCallback?.({ + content: [ + { + text: '3', + }, + ], + }); + streamingCallback?.({ + content: [ + { + text: '2', + }, + ], + }); + streamingCallback?.({ + content: [ + { + text: '1', + }, + ], + }); + return { + message: { + role: 'model', + content: [ + { + text: + 'Echo: ' + + request.messages + .map( + (m) => + (m.role === 'user' || m.role === 'model' + ? '' + : `${m.role}: `) + m.content.map((c) => c.text).join() + ) + .join(), + }, + ], + }, + finishReason: 'stop', + }; + } + ); +} diff --git a/js/plugins/express/tsconfig.json b/js/plugins/express/tsconfig.json new file mode 100644 index 000000000..596e2cf72 --- /dev/null +++ b/js/plugins/express/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src"] +} diff --git a/js/plugins/express/tsup.config.ts b/js/plugins/express/tsup.config.ts new file mode 100644 index 000000000..01dce0a6b --- /dev/null +++ b/js/plugins/express/tsup.config.ts @@ -0,0 +1,22 @@ +/** + * Copyright 2024 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 { defineConfig, Options } from 'tsup'; +import { defaultOptions } from '../../tsup.common'; + +export default defineConfig({ + ...(defaultOptions as Options), +}); diff --git a/js/plugins/express/typedoc.json b/js/plugins/express/typedoc.json new file mode 100644 index 000000000..35fed2c95 --- /dev/null +++ b/js/plugins/express/typedoc.json @@ -0,0 +1,3 @@ +{ + "entryPoints": ["src/index.ts"] +} diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 1dee877f4..d2c7db228 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -393,6 +393,40 @@ importers: specifier: ^4.9.0 version: 4.9.5 + plugins/express: + dependencies: + express: + specifier: ^4.21.1 + version: 4.21.1 + genkit: + specifier: workspace:* + version: link:../../genkit + devDependencies: + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 + '@types/node': + specifier: ^20.11.16 + version: 20.16.9 + get-port: + specifier: ^5.1.0 + version: 5.1.0 + npm-run-all: + specifier: ^4.1.5 + version: 4.1.5 + rimraf: + specifier: ^6.0.1 + version: 6.0.1 + tsup: + specifier: ^8.3.5 + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + tsx: + specifier: ^4.19.2 + version: 4.19.2 + typescript: + specifier: ^4.9.0 + version: 4.9.5 + plugins/firebase: dependencies: '@genkit-ai/google-cloud': @@ -1064,6 +1098,9 @@ importers: testapps/express: dependencies: + '@genkit-ai/express': + specifier: workspace:* + version: link:../../plugins/express '@genkit-ai/firebase': specifier: workspace:* version: link:../../plugins/firebase @@ -3786,6 +3823,10 @@ packages: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + engines: {node: '>= 0.6'} + cors@2.8.5: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} engines: {node: '>= 0.10'} @@ -4093,6 +4134,10 @@ packages: resolution: {integrity: sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==} engines: {node: '>= 0.10.0'} + express@4.21.1: + resolution: {integrity: sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==} + engines: {node: '>= 0.10.0'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -6908,7 +6953,7 @@ snapshots: async-mutex: 0.5.0 body-parser: 1.20.3 cors: 2.8.5 - express: 4.21.0 + express: 4.21.1 get-port: 5.1.0 json-schema: 0.4.0 zod: 3.23.8 @@ -8764,6 +8809,8 @@ snapshots: cookie@0.6.0: {} + cookie@0.7.1: {} + cors@2.8.5: dependencies: object-assign: 4.1.1 @@ -9178,6 +9225,42 @@ snapshots: transitivePeerDependencies: - supports-color + express@4.21.1: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.3 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.1 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.10 + proxy-addr: 2.0.7 + qs: 6.13.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.0 + serve-static: 1.16.2 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + extend@3.0.2: {} farmhash-modern@1.1.0: {} diff --git a/js/testapps/express/package.json b/js/testapps/express/package.json index 5653ecb4c..39e3c8de8 100644 --- a/js/testapps/express/package.json +++ b/js/testapps/express/package.json @@ -16,6 +16,7 @@ "license": "ISC", "dependencies": { "genkit": "workspace:*", + "@genkit-ai/express": "workspace:*", "@genkit-ai/firebase": "workspace:*", "@genkit-ai/googleai": "workspace:*", "genkitx-ollama": "workspace:*", diff --git a/js/testapps/express/src/index.ts b/js/testapps/express/src/index.ts index 6bfb49216..288b62e07 100644 --- a/js/testapps/express/src/index.ts +++ b/js/testapps/express/src/index.ts @@ -14,12 +14,21 @@ * limitations under the License. */ -import { googleAI } from '@genkit-ai/googleai'; +import { AuthPolicy, RequestWithAuth, handler } from '@genkit-ai/express'; +import { gemini15Flash, googleAI } from '@genkit-ai/googleai'; import { vertexAI } from '@genkit-ai/vertexai'; -import express, { Request, Response } from 'express'; +import express, { + ErrorRequestHandler, + Handler, + Request, + Response, +} from 'express'; import { genkit, run, z } from 'genkit'; +import { logger } from 'genkit/logging'; import { ollama } from 'genkitx-ollama'; +logger.setLogLevel('debug'); + const ai = genkit({ plugins: [ googleAI(), @@ -39,8 +48,8 @@ export const jokeFlow = ai.defineFlow( async (subject, streamingCallback) => { return await run('call-llm', async () => { const llmResponse = await ai.generate({ - prompt: `${subject}`, - model: 'ollama/gemma', + prompt: `tell me long joke about ${subject}`, + model: gemini15Flash, config: { temperature: 1, }, @@ -52,9 +61,39 @@ export const jokeFlow = ai.defineFlow( } ); +const auth: Handler = (req, resp, next) => { + const token = req.header('authorization'); + // pretend we check auth token + (req as RequestWithAuth).auth = { + username: token === 'open sesame' ? 'Ali Baba' : '40 thieves', + }; + next(); +}; + const app = express(); -const port = process.env.PORT || 5000; +app.use(express.json()); + +const authPolicies: Record = { + jokeFlow: ({ auth }) => { + if (auth.username != 'Ali Baba') { + throw new Error('unauthorized: ' + JSON.stringify(auth)); + } + }, +}; + +// curl http://localhost:5000/jokeFlow?stream=true -d '{"data": "banana"}' -H "content-type: application/json" -H "authorization: open sesame" +ai.flows.forEach((f) => { + app.post( + `/${f.name}`, + auth, + handler(f, { authPolicy: authPolicies[f.name] }) + ); +}); + +// curl http://localhost:5000/jokeHandler?stream=true -d '{"data": "banana"}' -H "content-type: application/json" +app.post('/jokeHandler', handler(jokeFlow)); +// curl http://localhost:5000/jokeWithFlow?subject=banana app.get('/jokeWithFlow', async (req: Request, res: Response) => { const subject = req.query['subject']?.toString(); if (!subject) { @@ -64,6 +103,7 @@ app.get('/jokeWithFlow', async (req: Request, res: Response) => { res.send(await jokeFlow(subject)); }); +// curl http://localhost:5000/jokeStream?subject=banana app.get('/jokeStream', async (req: Request, res: Response) => { const subject = req.query['subject']?.toString(); if (!subject) { @@ -76,13 +116,12 @@ app.get('/jokeStream', async (req: Request, res: Response) => { 'Transfer-Encoding': 'chunked', }); await ai.generate({ - prompt: `Tell me a joke about ${subject}`, - model: 'ollama/llama2', + prompt: `Tell me a long joke about ${subject}`, + model: gemini15Flash, config: { temperature: 1, }, streamingCallback: (c) => { - console.log(c.content[0].text); res.write(c.content[0].text); }, }); @@ -90,6 +129,15 @@ app.get('/jokeStream', async (req: Request, res: Response) => { res.end(); }); +const errorHandler: ErrorRequestHandler = (error, request, response, next) => { + if (error instanceof Error) { + console.log(error.stack); + } + return response.status(500).send(error); +}; +app.use(errorHandler); + +const port = process.env.PORT || 5000; app.listen(port, () => { console.log(`Example app listening on port ${port}`); }); From 339cea2f15a64490c465947471b8e5f25b2d60b6 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Tue, 10 Dec 2024 13:00:14 -0500 Subject: [PATCH 038/562] fix(js/plugins/firebase): fix imports for ESM compatibility in firebase auth (#1483) --- js/plugins/firebase/src/auth.ts | 4 ++-- js/plugins/firebase/tests/auth_test.ts | 2 +- js/testapps/esm/src/index.ts | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/js/plugins/firebase/src/auth.ts b/js/plugins/firebase/src/auth.ts index 2d164957b..18fe9819b 100644 --- a/js/plugins/firebase/src/auth.ts +++ b/js/plugins/firebase/src/auth.ts @@ -17,8 +17,8 @@ import { Response } from 'express'; import { DecodedIdToken, getAuth } from 'firebase-admin/auth'; import { __RequestWithAuth, z } from 'genkit'; -import { FunctionFlowAuth } from './functions'; -import { initializeAppIfNecessary } from './helpers'; +import { FunctionFlowAuth } from './functions.js'; +import { initializeAppIfNecessary } from './helpers.js'; export function firebaseAuth( policy: (user: DecodedIdToken, input: z.infer) => void | Promise diff --git a/js/plugins/firebase/tests/auth_test.ts b/js/plugins/firebase/tests/auth_test.ts index de3fca4f8..d382ec313 100644 --- a/js/plugins/firebase/tests/auth_test.ts +++ b/js/plugins/firebase/tests/auth_test.ts @@ -15,7 +15,7 @@ */ import { describe, expect, it } from '@jest/globals'; -import { firebaseAuth } from '../src/auth'; +import { firebaseAuth } from '../lib/auth.js'; describe('firebaseAuth', () => { it('config unset throws', async () => { diff --git a/js/testapps/esm/src/index.ts b/js/testapps/esm/src/index.ts index bf46617be..7958c0f97 100644 --- a/js/testapps/esm/src/index.ts +++ b/js/testapps/esm/src/index.ts @@ -18,6 +18,7 @@ import { checks } from '@genkit-ai/checks'; import { devLocalVectorstore } from '@genkit-ai/dev-local-vectorstore'; import { genkitEval } from '@genkit-ai/evaluator'; import { enableFirebaseTelemetry } from '@genkit-ai/firebase'; +import { firebaseAuth } from '@genkit-ai/firebase/auth'; import { enableGoogleCloudTelemetry } from '@genkit-ai/google-cloud'; import { googleAI } from '@genkit-ai/googleai'; import { vertexAI } from '@genkit-ai/vertexai'; @@ -42,5 +43,6 @@ pinecone; chroma; devLocalVectorstore; genkitEval; +firebaseAuth; export const ai = genkit({}); From 1ce1449fef603f73b628c5197dc7ac052e651c66 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:05:36 +0000 Subject: [PATCH 039/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@0.9.8 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 8139c2d6a..7e8274dae 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "0.9.8", "type": "commonjs", "scripts": { "check": "tsc", From 47de7243df585ca20102304bc95f65949573dcf5 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Tue, 10 Dec 2024 13:13:09 -0500 Subject: [PATCH 040/562] Revert "chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@0.9.8 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 7e8274dae..8139c2d6a 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "0.9.8", + "version": "0.9.7", "type": "commonjs", "scripts": { "check": "tsc", From e0a4ae6967322da226ffe3f066e3004ccbf1b34d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:50:49 +0000 Subject: [PATCH 041/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-dev.0 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 21f078a24..8f727bd6f 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "0.9.7", + "version": "1.0.0-dev.0", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From d9ec7bae591dfc2826e2d962a5c5df04b10c68c3 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:50:52 +0000 Subject: [PATCH 042/562] chore: bump genkit-cli version to genkit-cli@1.0.0-dev.0 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 09a3bf78e..0a0f7721f 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "0.9.7", + "version": "1.0.0-dev.0", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From 9c8a23dfafdecfe48e988dbd113b69ffce3c4e64 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:50:55 +0000 Subject: [PATCH 043/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-dev.0 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index 218029726..3365fcfaa 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From 80a5080875947001fc44d455244329d949d63dfe Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:56:11 +0000 Subject: [PATCH 044/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-dev.0 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 95dbcbd9d..7d57fff78 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From ede45ae36c5b1a779bd3d717c2a5b2dbbeaba59c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:56:14 +0000 Subject: [PATCH 045/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-dev.0 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index ba07730a2..9ff081f16 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From 06d1c1b85907143d7a16935e2a76824819ad9b71 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:56:17 +0000 Subject: [PATCH 046/562] chore: bump genkit version to genkit@1.0.0-dev.0 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index 3a6cd588f..49d736dca 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From dfd885b44d5c07801372759361244ba5d7012835 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:57:36 +0000 Subject: [PATCH 047/562] chore: bump genkitx-chromadb version to genkitx-chromadb1.0.0-dev.0 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index bf41f3d5f..d4b18eded 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From 9fccf0fc7f3ffd3639b14d143a4d260e02fe0ce9 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:57:39 +0000 Subject: [PATCH 048/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-dev.0 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 6f82bd7dd..07eb69aca 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From 6b64ae4acaea268b74018e99a15bb3be84176e69 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:57:41 +0000 Subject: [PATCH 049/562] chore: bump @genkit-ai/dotprompt version to @genkit-ai/dotprompt@1.0.0-dev.0 --- js/plugins/dotprompt/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dotprompt/package.json b/js/plugins/dotprompt/package.json index 2d6762313..04bca51b7 100644 --- a/js/plugins/dotprompt/package.json +++ b/js/plugins/dotprompt/package.json @@ -9,7 +9,7 @@ "prompting", "templating" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From d05a1e855ed36e1c8018bcd993176b981e58f4b3 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:57:44 +0000 Subject: [PATCH 050/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-dev.0 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 3e45ded09..48af72ef5 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From 16c800397e3ff56277dd2c3ea5081bc12eccc297 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:57:47 +0000 Subject: [PATCH 051/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-dev.0 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 8139c2d6a..2bd7c5469 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From 6cef208f695b597fd57f897b318e6829048c4847 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:57:50 +0000 Subject: [PATCH 052/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-dev.0 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index 3e8097d86..eb1c5ab37 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From 277dcab3c560c57616dc81cf777563ebcd7680bc Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:57:53 +0000 Subject: [PATCH 053/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-dev.0 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 02ad12d94..2d8738e11 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From 2d95c0f612e9244a55c26ce9bc2a526b3f1fe131 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:57:55 +0000 Subject: [PATCH 054/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-dev.0 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index ace9f5ef5..eb5d2e2fa 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From 1f053db480596b2352316d66d57e93447f104a6e Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:57:58 +0000 Subject: [PATCH 055/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-dev.0 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index a0e97d56c..1c00e7d53 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From f3094e6e062d677e5adc4b6b1e98e3bf162cdfb3 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:58:01 +0000 Subject: [PATCH 056/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-dev.0 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index d22908af7..53b18cae0 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From 40678e0802b68cd3c56284f03ee82b241e045006 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:58:04 +0000 Subject: [PATCH 057/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-dev.0 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 7f651e2e3..a1470cee3 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "0.9.7", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From aa7ca2ee5b6ae520af5ebac159a2348699ca1f3d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:58:07 +0000 Subject: [PATCH 058/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-dev.0 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index d0e38e6d3..8ee1e29c2 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "0.9.6", + "version": "1.0.0-dev.0", "type": "commonjs", "scripts": { "check": "tsc", From 03a1f3b1862b30822cbd454346bf0b451b8912c4 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 10 Dec 2024 18:58:10 +0000 Subject: [PATCH 059/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-dev.0 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index 22fe3b438..b821f3b5c 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "0.9.8", + "version": "1.0.0-dev.0", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From a6151875848553ea5dfc75521eef3cb87eae173d Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Tue, 10 Dec 2024 16:02:48 -0500 Subject: [PATCH 060/562] fix(evals): Bugbash - Error if empty dataset, fix message formatting (#1485) --- genkit-tools/common/src/eval/evaluate.ts | 10 ++++++++++ genkit-tools/common/src/eval/localFileDatasetStore.ts | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/genkit-tools/common/src/eval/evaluate.ts b/genkit-tools/common/src/eval/evaluate.ts index b02ad8580..22b17edf0 100644 --- a/genkit-tools/common/src/eval/evaluate.ts +++ b/genkit-tools/common/src/eval/evaluate.ts @@ -76,6 +76,10 @@ export async function runNewEvaluation( ); const datasetVersion = targetDatasetMetadata?.version; + if (dataset.length === 0) { + throw new Error(`Dataset ${datasetId} is empty`); + } + logger.info('Running inference...'); const evalDataset = await runInference({ manager, @@ -134,6 +138,9 @@ export async function runEvaluation(params: { augments?: EvalKeyAugments; }): Promise { const { manager, evaluatorActions, evalDataset, augments } = params; + if (evalDataset.length === 0) { + throw new Error('Cannot run evaluation, no data provided'); + } const evalRunId = randomUUID(); const scores: Record = {}; logger.info('Running evaluation...'); @@ -219,6 +226,9 @@ async function bulkRunAction(params: { reference: c.reference, testCaseId: c.testCaseId ?? generateTestCaseId(), })); + if (testCases.length === 0) { + throw new Error('Cannot run inference, no data provided'); + } let states: InferenceRunState[] = []; let evalInputs: EvalInput[] = []; diff --git a/genkit-tools/common/src/eval/localFileDatasetStore.ts b/genkit-tools/common/src/eval/localFileDatasetStore.ts index c8ba13dc1..4f96cfd25 100644 --- a/genkit-tools/common/src/eval/localFileDatasetStore.ts +++ b/genkit-tools/common/src/eval/localFileDatasetStore.ts @@ -159,7 +159,7 @@ export class LocalFileDatasetStore implements DatasetStore { this.generateFileName(datasetId) ); if (!fs.existsSync(filePath)) { - throw new Error(`Dataset not found for dataset ID {$id}`); + throw new Error(`Dataset not found for dataset ID ${datasetId}`); } return await readFile(filePath, 'utf8').then((data) => { return DatasetSchema.parse(JSON.parse(data)); From b5c58e3be22210ee5bdbe8440813bfa4332dfea5 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Tue, 10 Dec 2024 17:10:28 -0500 Subject: [PATCH 061/562] fix(js/core): make sure genkit logger is a true global singleton (#1488) --- js/core/src/logging.ts | 73 +++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/js/core/src/logging.ts b/js/core/src/logging.ts index d242708b3..c1d49f7e1 100644 --- a/js/core/src/logging.ts +++ b/js/core/src/logging.ts @@ -16,69 +16,68 @@ const LOG_LEVELS = ['debug', 'info', 'warn', 'error']; -class Logger { - logger: { - debug(...args: any); - info(...args: any); - warn(...args: any); - error(...args: any); - level: string; - }; +const loggerKey = '__genkit_logger'; - defaultLogger = { - shouldLog(targetLevel: string) { - return LOG_LEVELS.indexOf(this.level) <= LOG_LEVELS.indexOf(targetLevel); - }, - debug(...args: any) { - this.shouldLog('debug') && console.debug(...args); - }, - info(...args: any) { - this.shouldLog('info') && console.info(...args); - }, - warn(...args: any) { - this.shouldLog('warn') && console.warn(...args); - }, - error(...args: any) { - this.shouldLog('error') && console.error(...args); - }, - level: 'info', - }; +const _defaultLogger = { + shouldLog(targetLevel: string) { + return LOG_LEVELS.indexOf(this.level) <= LOG_LEVELS.indexOf(targetLevel); + }, + debug(...args: any) { + this.shouldLog('debug') && console.debug(...args); + }, + info(...args: any) { + this.shouldLog('info') && console.info(...args); + }, + warn(...args: any) { + this.shouldLog('warn') && console.warn(...args); + }, + error(...args: any) { + this.shouldLog('error') && console.error(...args); + }, + level: 'info', +}; - constructor() { - this.logger = this.defaultLogger; +function getLogger() { + if (!global[loggerKey]) { + global[loggerKey] = _defaultLogger; } + return global[loggerKey]; +} + +class Logger { + readonly defaultLogger = _defaultLogger; - async init(fn: any) { - this.logger = fn; + init(fn: any) { + global[loggerKey] = fn; } info(...args: any) { // eslint-disable-next-line prefer-spread - this.logger.info.apply(this.logger, args); + getLogger().info.apply(getLogger(), args); } debug(...args: any) { // eslint-disable-next-line prefer-spread - this.logger.debug.apply(this.logger, args); + getLogger().debug.apply(getLogger(), args); } error(...args: any) { // eslint-disable-next-line prefer-spread - this.logger.error.apply(this.logger, args); + getLogger().error.apply(getLogger(), args); } warn(...args: any) { // eslint-disable-next-line prefer-spread - this.logger.warn.apply(this.logger, args); + getLogger().warn.apply(getLogger(), args); } setLogLevel(level: 'error' | 'warn' | 'info' | 'debug') { - this.logger.level = level; + getLogger().level = level; } logStructured(msg: string, metadata: any) { - this.logger.info(msg, metadata); + getLogger().info(msg, metadata); } logStructuredError(msg: string, metadata: any) { - this.logger.error(msg, metadata); + getLogger().error(msg, metadata); } } From 6fea6b04e2790f2bf90e498eaca3d67f62081a39 Mon Sep 17 00:00:00 2001 From: Chris Ray Gill Date: Tue, 10 Dec 2024 18:29:08 -0500 Subject: [PATCH 062/562] docs: update Firebase plugin docs for 0.9 (#1399) * Updated doc * Formatting fixes * Some fixes --- docs/plugins/firebase.md | 230 +++++++++++++++++---------------------- 1 file changed, 101 insertions(+), 129 deletions(-) diff --git a/docs/plugins/firebase.md b/docs/plugins/firebase.md index 367bfe4f7..ba5901ee1 100644 --- a/docs/plugins/firebase.md +++ b/docs/plugins/firebase.md @@ -3,77 +3,63 @@ use https://github.com/firebase/firebase-tools/blob/master/templates/init/functi # Firebase plugin -The Firebase plugin provides several integrations with Firebase services: +The Firebase plugin provides integrations with Firebase services, allowing you to build intelligent and scalable AI applications. Key features include: -- Indexers and retrievers using Cloud Firestore vector store -- Trace storage using Cloud Firestore -- Flow deployment using Cloud Functions -- Authorization policies for Firebase Authentication users -- Telemetry export to [Google Cloud’s operation suite](https://cloud.google.com/products/operations) - - +- **Firestore Vector Store**: Use Firestore for indexing and retrieval with vector embeddings. +- **Cloud Functions**: Deploy flows as HTTPS-triggered functions. +- **Firebase Authentication**: Implement authorization policies. +- **Telemetry**: Export telemetry to [Google Cloud’s operations suite](https://cloud.google.com/products/operations) and see specialized views in the Firebase console ## Installation +Install the Firebase plugin with npm: + ```posix-terminal -npm i --save @genkit-ai/firebase +npm install @genkit-ai/firebase ``` ## Prerequisites -- All Firebase products require a Firebase project. You can create a new project - or enable Firebase in an existing Google Cloud project using the - [Firebase console](https://console.firebase.google.com/). -- In addition, if you want to deploy flows to Cloud Functions, you must - [upgrade your project](https://console.firebase.google.com/project/_/overview?purchaseBillingPlan=metered) - to the Blaze pay-as-you-go plan. -- If you want to run code locally that exports telemetry, you need the [Google Cloud CLI](https://cloud.google.com/sdk/docs/install) tool installed. +### Firebase Project Setup -## Configuration +1. All Firebase products require a Firebase project. You can create a new project or enable Firebase in an existing Google Cloud project using the [Firebase console](https://console.firebase.google.com/). +2. If deploying flows with Cloud Functions, [upgrade your Firebase project](https://console.firebase.google.com/project/_/overview?purchaseBillingPlan=metered) to the Blaze plan. +3. If you want to run code locally that exports telemetry, you need the [Google Cloud CLI](https://cloud.google.com/sdk/docs/install) tool installed. -### Project ID +### Firebase Admin SDK Initialization -To use this plugin, specify it when you initialize Genkit: +You must initialize the Firebase Admin SDK in your application. This is not handled automatically by the plugin. ```js -import { genkit } from 'genkit'; -import { firebase } from '@genkit-ai/firebase'; +import { initializeApp } from 'firebase-admin/app'; -const ai = genkit({ - plugins: [firebase({ projectId: "your-firebase-project" })], +initializeApp({ + projectId: 'your-project-id', }); ``` -The plugin requires you to specify your Firebase project ID. You can specify -your Firebase project ID in either of the following ways: +The plugin requires you to specify your Firebase project ID. You can specify your Firebase project ID in either of the following ways: -- Set `projectId` in the `firebase()` configuration object. +- Set `projectId` in the `initializeApp()` configuration object as shown in the snippet above. -- Set the `GCLOUD_PROJECT` environment variable. If you're running your flow - from a Google Cloud environment (Cloud Functions, Cloud Run, and so on), - `GCLOUD_PROJECT` is automatically set to the project ID of the environment. - - If you set `GCLOUD_PROJECT`, you can omit the configuration parameter: - `firebase()` +- Set the `GCLOUD_PROJECT` environment variable. If you're running your flow from a Google Cloud environment (Cloud Functions, Cloud Run, and so on), `GCLOUD_PROJECT` is automatically set to the project ID of the environment. + + If you set `GCLOUD_PROJECT`, you can omit the configuration parameter in `initializeApp()`. ### Credentials -To provide Firebase credentials, you also need to set up Google Cloud -Application Default Credentials. To specify your credentials: - -- If you're running your flow from a Google Cloud environment (Cloud Functions, - Cloud Run, and so on), this is set automatically. +To provide Firebase credentials, you also need to set up Google Cloud Application Default Credentials. To specify your credentials: -- For other environments: +- If you're running your flow from a Google Cloud environment (Cloud Functions, Cloud Run, and so on), this is set automatically. + +- For other environments: + + 1. Generate service account credentials for your Firebase project and download the JSON key file. You can do so on the [Service account](https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk) page of the Firebase console. + 2. Set the environment variable `GOOGLE_APPLICATION_CREDENTIALS` to the file path of the JSON file that contains your service account key, or you can set the environment variable `GCLOUD_SERVICE_ACCOUNT_CREDS` to the content of the JSON file. - 1. Generate service account credentials for your Firebase project and - download the JSON key file. You can do so on the - [Service account](https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk) - page of the Firebase console. - 1. Set the environment variable `GOOGLE_APPLICATION_CREDENTIALS` to the file - path of the JSON file that contains your service account key, or you can set the environment variable `GCLOUD_SERVICE_ACCOUNT_CREDS` to the content of the JSON file. +## Features and usage ### Telemetry @@ -87,27 +73,21 @@ import { enableFirebaseTelemetry } from '@genkit-ai/firebase'; enableFirebaseTelemetry(); ``` -Refer the the [Google Cloud plugin](google-cloud.md) documentation for all configuration options and the necessary APIs that need to be enabled on the project. - -## Usage - -This plugin provides several integrations with Firebase services, which you can -use together or individually. +Refer to the [Google Cloud plugin](google-cloud.md) documentation for all configuration options and the necessary APIs that need to be enabled on the project. -### Cloud Firestore vector store +### Cloud Firestore vector search You can use Cloud Firestore as a vector store for RAG indexing and retrieval. -This section contains information specific to the `firebase` plugin and Cloud -Firestore's vector search feature. -See the [Retrieval-augmented generation](../rag.md) page for a more detailed -discussion on implementing RAG using Genkit. +This section contains information specific to the `firebase` plugin and Cloud Firestore's vector search feature. See the [Retrieval-augmented generation](/../rag.md) page for a more detailed discussion on implementing RAG using Genkit. -#### Using GCLOUD_SERVICE_ACCOUNT_CREDS and Firestore +#### Using GCLOUD_SERVICE_ACCOUNT_CREDS and Firestore If you are using service account credentials by passing credentials directly via `GCLOUD_SERVICE_ACCOUNT_CREDS` and are also using Firestore as a vector store, you will need to pass credentials directly to the Firestore instance during initialization or the singleton may be initialized with application default credentials depending on plugin initialization order. -``` + + +```js import {initializeApp} from "firebase-admin/app"; import {getFirestore} from "firebase-admin/firestore"; @@ -121,62 +101,73 @@ if (process.env.GCLOUD_SERVICE_ACCOUNT_CREDS) { } ``` -#### Retrievers +#### Define a Firestore retriever -The `firebase` plugin provides a convenience function for defining Firestore -retrievers, `defineFirestoreRetriever()`: +Use `defineFirestoreRetriever()` to create a retriever for Firestore vector-based queries. ```js -import {defineFirestoreRetriever} from "@genkit-ai/firebase"; -import {retrieve} from "@genkit-ai/ai/retriever"; - -import {initializeApp} from "firebase-admin/app"; -import {getFirestore} from "firebase-admin/firestore"; +import { defineFirestoreRetriever } from '@genkit-ai/firebase'; +import { initializeApp } from 'firebase-admin/app'; +import { getFirestore } from 'firebase-admin/firestore'; const app = initializeApp(); const firestore = getFirestore(app); -const yourRetrieverRef = defineFirestoreRetriever({ - name: "yourRetriever", - firestore: getFirestore(app), - collection: "yourCollection", - contentField: "yourDataChunks", - vectorField: "embedding", - embedder: textEmbeddingGecko, // Import from '@genkit-ai/googleai' or '@genkit-ai/vertexai' - distanceMeasure: "COSINE", // "EUCLIDEAN", "DOT_PRODUCT", or "COSINE" (default) +const retriever = defineFirestoreRetriever(ai, { + name: 'exampleRetriever', + firestore, + collection: 'documents', + contentField: 'text', // Field containing document content + vectorField: 'embedding', // Field containing vector embeddings + embedder: yourEmbedderInstance, // Embedder to generate embeddings + distanceMeasure: 'COSINE', // Default is 'COSINE'; other options: 'EUCLIDEAN', 'DOT_PRODUCT' }); ``` -To use it, pass it to the `ai.retrieve()` function: +#### Retrieve documents + +To retrieve documents using the defined retriever, pass the retriever instance and query options to `ai.retrieve`. ```js const docs = await ai.retrieve({ - retriever: yourRetrieverRef, - query: "look for something", - options: { limit: 5 }, + retriever, + query: 'search query', + options: { + limit: 5, // Options: Return up to 5 documents + where: { category: 'example' }, // Optional: Filter by field-value pairs + collection: 'alternativeCollection', // Optional: Override default collection + }, }); ``` -Available retrieval options include: +#### Available Retrieval Options + +The following options can be passed to the `options` field in `ai.retrieve`: + +- **`limit`**: *(number)* + Specify the maximum number of documents to retrieve. Default is `10`. + +- **`where`**: *(Record\)* + Add additional filters based on Firestore fields. Example: -- `limit`: Specify the number of matching results to return. -- `where`: Field/value pairs to match (e.g. `{category: 'food'}`) in addition to vector search. -- `collection`: Override the default collection to search for e.g. subcollection search. + ```js + where: { category: 'news', status: 'published' } + ``` -#### Indexing and Embedding +- **`collection`**: *(string)* + Override the default collection specified in the retriever configuration. This is useful for querying subcollections or dynamically switching between collections. -To populate your Firestore collection, use an embedding generator along with the -Admin SDK. For example, the menu ingestion script from the -[Retrieval-augmented generation](../rag.md) page could be adapted for Firestore -in the following way: +#### Populate Firestore with Embeddings + +To populate your Firestore collection, use an embedding generator along with the Admin SDK. For example, the menu ingestion script from the [Retrieval-augmented generation](http://../rag.md) page could be adapted for Firestore in the following way: -```ts +```js import { genkit } from 'genkit'; import { vertexAI, textEmbedding004 } from "@genkit-ai/vertexai"; @@ -238,45 +229,33 @@ async function extractTextFromPdf(filePath: string) { } ``` -Firestore depends on indexes to provide fast and efficient querying on -collections. (Note that "index" here refers to database indexes, and not -Genkit's indexer and retriever abstractions.) +Firestore depends on indexes to provide fast and efficient querying on collections. (Note that "index" here refers to database indexes, and not Genkit's indexer and retriever abstractions.) -The prior example requires the `embedding` field to be indexed to -work. To create the index: - -- Run the `gcloud` command described in the - [Create a single-field vector index](https://firebase.google.com/docs/firestore/vector-search?authuser=0#create_and_manage_vector_indexes) - section of the Firestore docs. +The prior example requires the `embedding` field to be indexed to work. To create the index: +- Run the `gcloud` command described in the [Create a single-field vector index](https://firebase.google.com/docs/firestore/vector-search?authuser=0#create_and_manage_vector_indexes) section of the Firestore docs. + The command looks like the following: - ```posix-terminal + ``` gcloud alpha firestore indexes composite create --project=your-project-id \ --collection-group=yourCollectionName --query-scope=COLLECTION \ --field-config=vector-config='{"dimension":"768","flat": "{}"}',field-path=yourEmbeddingField ``` - However, the correct indexing configuration depends on the queries you will - make and the embedding model you're using. + However, the correct indexing configuration depends on the queries you will make and the embedding model you're using. -- Alternatively, call `ai.retrieve()` and Firestore will throw an error with the - correct command to create the index. + +- Alternatively, call `ai.retrieve()` and Firestore will throw an error with the correct command to create the index. #### Learn more -- See the [Retrieval-augmented generation](../rag.md) page for a general - discussion on indexers and retrievers in Genkit. -- See [Search with vector embeddings](https://firebase.google.com/docs/firestore/vector-search) - in the Cloud Firestore docs for more on the vector search feature. -### Cloud Functions +- See the [Retrieval-augmented generation](http://../rag.md) page for a general discussion on indexers and retrievers in Genkit. +- See [Search with vector embeddings](https://firebase.google.com/docs/firestore/vector-search) in the Cloud Firestore docs for more on the vector search feature. + +### Deploy flows as Cloud Functions -The plugin provides the `onFlow()` constructor, which creates a flow backed by a -Cloud Functions for Firebase HTTPS-triggered function. These functions conform -to Firebase's -[callable function interface](https://firebase.google.com/docs/functions/callable-reference) and you can use the -[Cloud Functions client SDKs](https://firebase.google.com/docs/functions/callable?gen=2nd#call_the_function) -to call them. +The plugin provides the `onFlow()` constructor, which creates a flow backed by a Cloud Functions for Firebase HTTPS-triggered function. These functions conform to Firebase's [callable function interface](https://firebase.google.com/docs/functions/callable-reference) and you can use the [Cloud Functions client SDKs](https://firebase.google.com/docs/functions/callable?gen=2nd#call_the_function) to call them. @@ -299,14 +278,13 @@ export const exampleFlow = onFlow( Deploy your flow using the Firebase CLI: -```posix-terminal +``` firebase deploy --only functions ``` The `onFlow()` function has some options not present in `defineFlow()`: -- `httpsOptions`: an [`HttpsOptions`](https://firebase.google.com/docs/reference/functions/2nd-gen/node/firebase-functions.https.httpsoptions) - object used to configure your Cloud Function: +- `httpsOptions`: an [`HttpsOptions`](https://firebase.google.com/docs/reference/functions/2nd-gen/node/firebase-functions.https.httpsoptions) object used to configure your Cloud Function: @@ -326,17 +304,15 @@ The `onFlow()` function has some options not present in `defineFlow()`: ); ``` -- `enforceAppCheck`: when `true`, reject requests with missing or invalid [App Check](https://firebase.google.com/docs/app-check) - tokens. - -- `consumeAppCheckToken`: when `true`, invalidate the App Check token after verifying it. - +- `enforceAppCheck`: when `true`, reject requests with missing or invalid [App Check](https://firebase.google.com/docs/app-check) tokens. + +- `consumeAppCheckToken`: when `true`, invalidate the App Check token after verifying it. + See [Replay protection](https://firebase.google.com/docs/app-check/cloud-functions#replay-protection). -### Firebase Auth +### Firebase Authentication -This plugin provides a helper function to create authorization policies around -Firebase Auth: +This plugin provides a helper function to create authorization policies around Firebase Auth: @@ -357,10 +333,6 @@ export const exampleFlow = onFlow( ); ``` -To define an auth policy, provide `firebaseAuth()` with a callback function that -takes a -[`DecodedIdToken`](https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.decodedidtoken) -as its only parameter. In this function, examine the user token and throw an -error if the user fails to meet any of the criteria you want to require. +To define an auth policy, provide `firebaseAuth()` with a callback function that takes a [`DecodedIdToken`](https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.decodedidtoken) as its only parameter. In this function, examine the user token and throw an error if the user fails to meet any of the criteria you want to require. -See [Authorization and integrity](../auth.md) for a more thorough discussion of this topic. +See [Authorization and integrity](http://../auth.md) for a more thorough discussion of this topic. \ No newline at end of file From 009239b347937fea440096874d59fe9fc0c6dc51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Louise=20K=C3=BCmmel?= Date: Wed, 11 Dec 2024 09:40:59 +0100 Subject: [PATCH 063/562] breaking(go): upgrade ergonomics of prompt definition and calling (#1447) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit type promptInput struct { Style string } type promptOutput struct { Style string Message string } myPrompt, err := dotprompt.Define( "myPrompt", "Talk in the style of a {{style}}.", dotprompt.WithInputType(promptInput{ Style: "pirate" }), dotprompt.WithOutputType(promptOutput{}), dotprompt.WithTools(genkit.Prompt("commonSayings").AsTool()), dotprompt.WithDefaultModel(googleai.Model("gemini-1.5-flash”)), dotprompt.WithDefaultConfig(&ai.GenerationCommonConfig{}), ) if err != nil { log.Fatal(err) } resp, err := myPrompt.Generate( ctx, dotprompt.WithInput(inputStruct{ Style: "astronaut" }), dotprompt.WithModel(googleai.Model("gemini-1.5-pro")), dotprompt.WithStreaming(func(context.Context, string) error { ... }) ) if err != nil { log.Fatal(err) } --- go/core/action.go | 5 +- go/internal/doc-snippets/dotprompt.go | 36 ++- go/plugins/dotprompt/dotprompt.go | 162 +++++++++++-- go/plugins/dotprompt/dotprompt_test.go | 304 +++++++++++++++++++++++++ go/plugins/dotprompt/genkit.go | 150 ++++++++++-- go/plugins/dotprompt/genkit_test.go | 147 +++++++++++- go/plugins/dotprompt/render.go | 4 +- go/samples/coffee-shop/main.go | 46 ++-- go/samples/menu/s01.go | 14 +- go/samples/menu/s02.go | 13 +- go/samples/menu/s03.go | 14 +- go/samples/menu/s04.go | 22 +- go/samples/menu/s05.go | 43 ++-- go/samples/rag/main.go | 13 +- 14 files changed, 810 insertions(+), 163 deletions(-) diff --git a/go/core/action.go b/go/core/action.go index e22c6a399..48757820b 100644 --- a/go/core/action.go +++ b/go/core/action.go @@ -18,6 +18,7 @@ import ( "context" "encoding/json" "fmt" + "reflect" "time" "github.com/firebase/genkit/go/core/logger" @@ -140,7 +141,9 @@ func newAction[In, Out, Stream any]( var i In var o Out if inputSchema == nil { - inputSchema = base.InferJSONSchema(i) + if reflect.ValueOf(i).Kind() != reflect.Invalid { + inputSchema = base.InferJSONSchema(i) + } } return &Action[In, Out, Stream]{ name: name, diff --git a/go/internal/doc-snippets/dotprompt.go b/go/internal/doc-snippets/dotprompt.go index c3ea9fc44..10b73fa88 100644 --- a/go/internal/doc-snippets/dotprompt.go +++ b/go/internal/doc-snippets/dotprompt.go @@ -55,13 +55,11 @@ func dot01() error { } response, err := prompt.Generate( ctx, - &dotprompt.PromptRequest{ - Variables: GreetingPromptInput{ - Location: "the beach", - Style: "a fancy pirate", - Name: "Ed", - }, - }, + dotprompt.WithInput(GreetingPromptInput{ + Location: "the beach", + Style: "a fancy pirate", + Name: "Ed", + }), nil, ) if err != nil { @@ -96,17 +94,15 @@ func dot02() { response, err := prompt.Generate( context.Background(), - &dotprompt.PromptRequest{ - Variables: GreetingPromptInput{ - Location: "the beach", - Style: "a fancy pirate", - Name: "Ed", - }, - Model: "vertexai/gemini-1.5-flash", - Config: &ai.GenerationCommonConfig{ - Temperature: 1.0, - }, - }, + dotprompt.WithInput(GreetingPromptInput{ + Location: "the beach", + Style: "a fancy pirate", + Name: "Ed", + }), + dotprompt.WithModelName("vertexai/gemini-1.5-flash"), + dotprompt.WithConfig(&ai.GenerationCommonConfig{ + Temperature: 1.0, + }), nil, ) // [END dot02] @@ -135,9 +131,9 @@ func dot03() error { } response, err := describeImagePrompt.Generate( context.Background(), - &dotprompt.PromptRequest{Variables: DescribeImagePromptInput{ + dotprompt.WithInput(DescribeImagePromptInput{ PhotoUrl: dataURI, - }}, + }), nil, ) // [END dot03] diff --git a/go/plugins/dotprompt/dotprompt.go b/go/plugins/dotprompt/dotprompt.go index cda6dc735..ea9116540 100644 --- a/go/plugins/dotprompt/dotprompt.go +++ b/go/plugins/dotprompt/dotprompt.go @@ -26,9 +26,11 @@ import ( "os" "path/filepath" "slices" + "strconv" "github.com/aymerick/raymond" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/internal/base" "github.com/invopop/jsonschema" "gopkg.in/yaml.v3" ) @@ -85,16 +87,12 @@ type Config struct { ModelName string // The Model to use. - // If this is non-nil, Model should be the empty string. + // If this is set, ModelName should be an empty string. Model ai.Model // TODO: document Tools []ai.Tool - // Number of candidates to generate when passing the prompt - // to a model. If 0, uses 1. - Candidates int - // Details for the model. GenerationConfig *ai.GenerationCommonConfig @@ -102,7 +100,7 @@ type Config struct { InputSchema *jsonschema.Schema // Default input variable values - VariableDefaults map[string]any + DefaultInput map[string]any // Desired output format. OutputFormat ai.OutputFormat @@ -114,6 +112,9 @@ type Config struct { Metadata map[string]any } +// PromptOption configures params for the prompt +type PromptOption func(p *Prompt) error + // Open opens and parses a dotprompt file. // The name is a base file name, without the ".prompt" extension. func Open(name string) (*Prompt, error) { @@ -234,9 +235,8 @@ func parseFrontmatter(data []byte) (name string, c Config, rest []byte, err erro Variant: fy.Variant, ModelName: fy.Model, Tools: tools, - Candidates: fy.Candidates, GenerationConfig: fy.Config, - VariableDefaults: fy.Input.Default, + DefaultInput: fy.Input.Default, Metadata: fy.Metadata, } @@ -284,11 +284,21 @@ func parseFrontmatter(data []byte) (name string, c Config, rest []byte, err erro // Define creates and registers a new Prompt. This can be called from code that // doesn't have a prompt file. -func Define(name, templateText string, cfg Config) (*Prompt, error) { - p, err := New(name, templateText, cfg) +func Define(name, templateText string, opts ...PromptOption) (*Prompt, error) { + p, err := New(name, templateText, Config{}) if err != nil { return nil, err } + + for _, with := range opts { + err := with(p) + if err != nil { + return nil, err + } + } + + // TODO Inherit model from genkit instance + p.Register() return p, nil } @@ -297,9 +307,6 @@ func Define(name, templateText string, cfg Config) (*Prompt, error) { // This may be used for testing or for direct calls not using the // genkit action and flow mechanisms. func New(name, templateText string, cfg Config) (*Prompt, error) { - if cfg.ModelName == "" && cfg.Model == nil { - return nil, errors.New("dotprompt.New: config must specify either ModelName or Model") - } if cfg.ModelName != "" && cfg.Model != nil { return nil, errors.New("dotprompt.New: config must specify exactly one of ModelName and Model") } @@ -321,3 +328,132 @@ func sortSchemaSlices(s *jsonschema.Schema) { sortSchemaSlices(s.Items) } } + +// WithTools adds tools to the prompt. +func WithTools(tools ...ai.Tool) PromptOption { + return func(p *Prompt) error { + if p.Config.Tools != nil { + return errors.New("dotprompt.WithTools: cannot set tools more than once") + } + + var toolSlice []ai.Tool + toolSlice = append(toolSlice, tools...) + p.Tools = toolSlice + return nil + } +} + +// WithDefaultConfig adds default model configuration. +func WithDefaultConfig(config *ai.GenerationCommonConfig) PromptOption { + return func(p *Prompt) error { + if p.Config.GenerationConfig != nil { + return errors.New("dotprompt.WithDefaultConfig: cannot set Config more than once") + } + p.Config.GenerationConfig = config + return nil + } +} + +// WithInputType uses the type provided to derive the input schema. +// If passing eg. a struct with values, the struct definition will serve as the schema, the values will serve as defaults if no input is given at generation time. +func WithInputType(input any) PromptOption { + return func(p *Prompt) error { + if p.Config.InputSchema != nil { + return errors.New("dotprompt.WithInputType: cannot set InputType more than once") + } + + // Handle primitives, default to "value" as key + // No default necessary, assumed type to be struct + switch v := input.(type) { + case int: + input = map[string]any{"value": strconv.Itoa(v)} + case float32: + case float64: + input = map[string]any{"value": fmt.Sprintf("%f", v)} + case string: + input = map[string]any{"value": v} + // Pass map directly + case map[string]any: + input = v + } + + p.Config.InputSchema = base.InferJSONSchemaNonReferencing(input) + + // Set values as default input + defaultInput := base.SchemaAsMap(p.Config.InputSchema) + data, err := json.Marshal(input) + if err != nil { + return err + } + + err = json.Unmarshal(data, &defaultInput) + if err != nil { + return err + } + + p.Config.DefaultInput = defaultInput + return nil + } +} + +// WithOutputType uses the type provided to derive the output schema. +func WithOutputType(output any) PromptOption { + return func(p *Prompt) error { + if p.Config.OutputSchema != nil { + return errors.New("dotprompt.WithOutputType: cannot set OutputType more than once") + } + + p.Config.OutputSchema = base.InferJSONSchemaNonReferencing(output) + p.Config.OutputFormat = ai.OutputFormatJSON + + return nil + } +} + +// WithOutputFormat adds the desired output format to the prompt +func WithOutputFormat(format ai.OutputFormat) PromptOption { + return func(p *Prompt) error { + if p.Config.OutputFormat != "" && p.Config.OutputFormat != format { + return errors.New("dotprompt.WithOutputFormat: OutputFormat does not match set OutputSchema") + } + if format == ai.OutputFormatJSON && p.Config.OutputSchema == nil { + return errors.New("dotprompt.WithOutputFormat: to set OutputFormat to JSON, OutputSchema must be set") + } + + p.Config.OutputFormat = format + return nil + } +} + +// WithMetadata adds arbitrary metadata. +func WithMetadata(metadata map[string]any) PromptOption { + return func(p *Prompt) error { + if p.Config.Metadata != nil { + return errors.New("dotprompt.WithMetadata: cannot set Metadata more than once") + } + p.Config.Metadata = metadata + return nil + } +} + +// WithDefaultModel adds the default Model to use. +func WithDefaultModel(model ai.Model) PromptOption { + return func(p *Prompt) error { + if p.Config.ModelName != "" || p.Config.Model != nil { + return errors.New("dotprompt.WithDefaultModel: config must specify exactly once, either ModelName or Model") + } + p.Config.Model = model + return nil + } +} + +// WithDefaultModelName adds the name of the default Model to use. +func WithDefaultModelName(name string) PromptOption { + return func(p *Prompt) error { + if p.Config.ModelName != "" || p.Config.Model != nil { + return errors.New("dotprompt.WithDefaultModelName: config must specify exactly once, either ModelName or Model") + } + p.Config.ModelName = name + return nil + } +} diff --git a/go/plugins/dotprompt/dotprompt_test.go b/go/plugins/dotprompt/dotprompt_test.go index 41480f43a..a7fdf3be8 100644 --- a/go/plugins/dotprompt/dotprompt_test.go +++ b/go/plugins/dotprompt/dotprompt_test.go @@ -15,13 +15,31 @@ package dotprompt import ( + "context" "encoding/json" "testing" + "github.com/firebase/genkit/go/ai" "github.com/google/go-cmp/cmp" "github.com/invopop/jsonschema" ) +type InputOutput struct { + Text string `json:"text"` +} + +func testTool(name string) *ai.ToolDef[struct{ Test string }, string] { + return ai.DefineTool(name, "use when need to execute a test", + func(ctx context.Context, input struct { + Test string + }) (string, error) { + return input.Test, nil + }, + ) +} + +var testModel = ai.DefineModel("defineoptions", "test", nil, testGenerate) + func TestPrompts(t *testing.T) { SetDirectory("testdata") @@ -133,6 +151,292 @@ func TestPrompts(t *testing.T) { } } +func TestOptionsPatternDefine(t *testing.T) { + t.Run("WithTypesAndModel", func(t *testing.T) { + dotPrompt, err := Define( + "TestTypes", + "TestTypes", + WithTools(testTool("testOptionsPatternDefine")), + WithDefaultConfig(&ai.GenerationCommonConfig{}), + WithInputType(InputOutput{}), + WithOutputType(InputOutput{}), + WithMetadata(map[string]any{"test": "test"}), + WithDefaultModel(testModel), + ) + if err != nil { + t.Fatal(err) + } + + if dotPrompt.Tools == nil { + t.Error("tools not set") + } + if dotPrompt.Config.GenerationConfig == nil { + t.Error("generationConfig not set") + } + if dotPrompt.Config.InputSchema == nil { + t.Error("inputschema not set") + } + if dotPrompt.Config.OutputSchema == nil { + t.Error("outputschema not set") + } + if dotPrompt.Config.OutputFormat == "" { + t.Error("outputschema not set") + } + if dotPrompt.Config.DefaultInput == nil { + t.Error("default input not set") + } + if dotPrompt.Config.Metadata == nil { + t.Error("metadata not set") + } + if dotPrompt.Config.Model == nil { + t.Error("model not set") + } + // TODO Inherit from model in genkit + // if dotPrompt.Config.Model == nil { + // t.Error("model not inherited") + // } + }) + + t.Run("WithDefaultMap", func(t *testing.T) { + dotPrompt, err := Define( + "TestDefaultMap", + "TestDefaultMap", + WithInputType(map[string]any{"test": "test"}), + ) + if err != nil { + t.Fatal(err) + } + if dotPrompt.Config.InputSchema == nil { + t.Error("inputschema not set") + } + if dotPrompt.Config.DefaultInput == nil { + t.Error("Input default not set") + } + if dotPrompt.Config.DefaultInput["test"] != "test" { + t.Error("Input default incorrect") + } + }) + + t.Run("WithDefaultStruct", func(t *testing.T) { + dotPrompt, err := Define( + "TestDefaultStruct", + "TestDefaultStruct", + WithInputType(InputOutput{Text: "test"}), + ) + if err != nil { + t.Fatal(err) + } + if dotPrompt.Config.InputSchema == nil { + t.Error("inputschema not set") + } + if dotPrompt.Config.DefaultInput == nil { + t.Error("Input default not set") + } + if dotPrompt.Config.DefaultInput["text"] != "test" { + t.Error("Input default incorrect") + } + }) +} + +func TestOutputFormat(t *testing.T) { + var tests = []struct { + name string + output any + format ai.OutputFormat + err bool + }{ + { + name: "mismatch", + output: InputOutput{}, + format: ai.OutputFormatText, + err: true, + }, + { + name: "json", + output: InputOutput{}, + format: ai.OutputFormatJSON, + err: false, + }, + { + name: "text", + format: ai.OutputFormatText, + err: false, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var err error + + if test.output == nil { + _, err = Define( + "aModel", + "aModel", + WithInputType(InputOutput{Text: "test"}), + WithOutputFormat(test.format), + ) + } else { + _, err = Define( + "bModel", + "bModel", + WithInputType(InputOutput{Text: "test"}), + WithOutputType(test.output), + WithOutputFormat(test.format), + ) + } + if err != nil { + if test.err { + t.Logf("got expected error %v", err) + return + } + t.Fatal(err) + } + }) + } +} + +func TestInputFormat(t *testing.T) { + type hello struct { + Name string `json:"name"` + } + + var tests = []struct { + name string + templateText string + inputType any + input map[string]any + render string + }{ + { + name: "noInput", + templateText: "hello world", + input: nil, + render: "hello world", + }, + { + name: "structInput", + templateText: "hello {{name}}", + inputType: hello{}, + input: map[string]any{"name": "world"}, + render: "hello world", + }, + { + name: "stringInput", + templateText: "hello {{input}}", + inputType: "world", + input: map[string]any{"input": "world"}, + render: "hello world", + }, + { + name: "intInput", + templateText: "hello {{input}}", + inputType: 1, + input: map[string]any{"input": 1}, + render: "hello 1", + }, + { + name: "floatInput", + templateText: "the value of pi is {{input}}", + inputType: 3.14159, + input: map[string]any{"input": 3.14159}, + render: "the value of pi is 3.14159", + }, + { + name: "mapInput", + templateText: "hello {{name}}", + inputType: map[string]any{"name": "world"}, + input: map[string]any{"name": "world"}, + render: "hello world", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var err error + var p *Prompt + + if test.inputType != nil { + p, err = Define( + test.name, + test.templateText, + WithInputType(test.inputType), + ) + } else { + p, err = Define( + "inputFormat", + test.templateText, + ) + } + + if err != nil { + t.Fatal(err) + } + + txt, err := p.RenderText(test.input) + if err != nil { + t.Fatal(err) + } + + if txt != test.render { + t.Errorf("got %q want %q", txt, test.render) + } + }) + } +} + +func TestPromptOptions(t *testing.T) { + var tests = []struct { + name string + with PromptOption + }{ + { + name: "WithTools", + with: WithTools(testTool("testPromptOptions")), + }, + { + name: "WithDefaultConfig", + with: WithDefaultConfig(&ai.GenerationCommonConfig{}), + }, + { + name: "WithInputType", + with: WithInputType(InputOutput{}), + }, + { + name: "WithOutputType", + with: WithOutputType(InputOutput{}), + }, + { + name: "WithOutputFormat", + with: WithOutputFormat(ai.OutputFormatJSON), + }, + { + name: "WithMetadata", + with: WithMetadata(map[string]any{"test": "test"}), + }, + { + name: "WithDefaultModelName", + with: WithDefaultModelName("defineoptions/test"), + }, + { + name: "WithDefaultModel", + with: WithDefaultModel(testModel), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, err := Define( + "TestWith", + "TestWith", + test.with, + test.with, + ) + + if err == nil { + t.Errorf("%s could be set twice", test.name) + } + }) + } +} + // cmpSchema compares schema values, returning the output of cmp.Diff. // There doesn't seem to be a good way to compare jsonschema.Schema values. // They have unexported fields and fields of type orderedmap.OrderedMap. diff --git a/go/plugins/dotprompt/genkit.go b/go/plugins/dotprompt/genkit.go index 43aaf842b..866a929aa 100644 --- a/go/plugins/dotprompt/genkit.go +++ b/go/plugins/dotprompt/genkit.go @@ -31,18 +31,22 @@ import ( type PromptRequest struct { // Input fields for the prompt. If not nil this should be a struct // or pointer to a struct that matches the prompt's input schema. - Variables any `json:"variables,omitempty"` - // Number of candidates to return; if 0, will be taken - // from the prompt config; if still 0, will use 1. - Candidates int `json:"candidates,omitempty"` + Input any `json:"input,omitempty"` // Model configuration. If nil will be taken from the prompt config. Config *ai.GenerationCommonConfig `json:"config,omitempty"` // Context to pass to model, if any. Context []any `json:"context,omitempty"` // The model to use. This overrides any model specified by the prompt. - Model string `json:"model,omitempty"` + Model ai.Model `json:"model,omitempty"` + // The name of the model to use. This overrides any model specified by the prompt. + ModelName string `json:"modelname,omitempty"` + // Streaming callback function + Stream ai.ModelStreamingCallback } +// GenerateOption configures params for Generate function +type GenerateOption func(p *PromptRequest) error + // buildVariables returns a map holding prompt field values based // on a struct or a pointer to a struct. The struct value should have // JSON tags that correspond to the Prompt's input schema. @@ -154,7 +158,9 @@ func (p *Prompt) Register() error { } // TODO: Undo clearing of the Version once Monaco Editor supports newer than JSON schema draft-07. - p.InputSchema.Version = "" + if p.InputSchema != nil { + p.InputSchema.Version = "" + } metadata := map[string]any{ "prompt": map[string]any{ @@ -174,15 +180,23 @@ func (p *Prompt) Register() error { // the prompt. // // This implements the [ai.Prompt] interface. -func (p *Prompt) Generate(ctx context.Context, pr *PromptRequest, cb func(context.Context, *ai.ModelResponseChunk) error) (*ai.ModelResponse, error) { +func (p *Prompt) Generate(ctx context.Context, opts ...GenerateOption) (*ai.ModelResponse, error) { tracing.SetCustomMetadataAttr(ctx, "subtype", "prompt") + var pr PromptRequest + + for _, with := range opts { + err := with(&pr) + if err != nil { + return nil, err + } + } - var genReq *ai.ModelRequest + var mr *ai.ModelRequest var err error if p.prompt != nil { - genReq, err = p.prompt.Render(ctx, pr.Variables) + mr, err = p.prompt.Render(ctx, pr.Input) } else { - genReq, err = p.buildRequest(ctx, pr.Variables) + mr, err = p.buildRequest(ctx, pr.Input) } if err != nil { return nil, err @@ -190,17 +204,25 @@ func (p *Prompt) Generate(ctx context.Context, pr *PromptRequest, cb func(contex // Let some fields in pr override those in the prompt config. if pr.Config != nil { - genReq.Config = pr.Config + mr.Config = pr.Config } if len(pr.Context) > 0 { - genReq.Context = pr.Context + mr.Context = pr.Context + } + + // Setting the model on generate, overrides the model defined on the prompt + var model ai.Model + if p.Model != nil { + model = p.Model + } + if pr.Model != nil { + model = pr.Model } - model := p.Model if model == nil { modelName := p.ModelName - if pr.Model != "" { - modelName = pr.Model + if pr.ModelName != "" { + modelName = pr.ModelName } if modelName == "" { return nil, errors.New("dotprompt execution: model not specified") @@ -216,10 +238,106 @@ func (p *Prompt) Generate(ctx context.Context, pr *PromptRequest, cb func(contex } } - resp, err := model.Generate(ctx, genReq, cb) + resp, err := model.Generate(ctx, mr, pr.Stream) if err != nil { return nil, err } return resp, nil } + +// GenerateText runs generate request for this prompt. Returns generated text only. +func (p *Prompt) GenerateText(ctx context.Context, opts ...GenerateOption) (string, error) { + res, err := p.Generate(ctx, opts...) + if err != nil { + return "", err + } + + return res.Text(), nil +} + +// GenerateData runs generate request for this prompt. Returns ModelResponse struct. +// TODO: Stream GenerateData with partial JSON +func (p *Prompt) GenerateData(ctx context.Context, value any, opts ...GenerateOption) (*ai.ModelResponse, error) { + with := WithOutputType(value) + err := with(p) + if err != nil { + return nil, err + } + + resp, err := p.Generate(ctx, opts...) + if err != nil { + return nil, err + } + err = resp.UnmarshalOutput(value) + if err != nil { + return nil, err + } + return resp, nil +} + +// WithInput adds input to pass to the model. +func WithInput(input any) GenerateOption { + return func(p *PromptRequest) error { + if p.Input != nil { + return errors.New("dotprompt.WithInput: cannot set Input more than once") + } + p.Input = input + return nil + } +} + +// WithConfig adds model configuration. If nil will be taken from the prompt config. +func WithConfig(config *ai.GenerationCommonConfig) GenerateOption { + return func(p *PromptRequest) error { + if p.Config != nil { + return errors.New("dotprompt.WithConfig: cannot set Config more than once") + } + p.Config = config + return nil + } +} + +// WithContext add context to pass to model, if any. +func WithContext(context []any) GenerateOption { + return func(p *PromptRequest) error { + if p.Context != nil { + return errors.New("dotprompt.WithContext: cannot set Context more than once") + } + p.Context = context + return nil + } +} + +// WithModel adds the Model to use. This overrides any model specified by the prompt. +func WithModel(model ai.Model) GenerateOption { + return func(p *PromptRequest) error { + if p.ModelName != "" || p.Model != nil { + return errors.New("dotprompt.WithModel: Model must be specified exactly once, either ModelName or Model") + } + p.Model = model + return nil + } +} + +// WithModelName adds the name of the Model to use. This overrides any model specified by the prompt. +func WithModelName(model string) GenerateOption { + return func(p *PromptRequest) error { + if p.ModelName != "" || p.Model != nil { + return errors.New("dotprompt.WithModelName: Model must be specified exactly once, either ModelName or Model") + } + p.ModelName = model + return nil + } +} + +// WithStreaming adds a streaming callback to the generate request. +func WithStreaming(cb ai.ModelStreamingCallback) GenerateOption { + return func(g *PromptRequest) error { + if g.Stream != nil { + return errors.New("dotprompt.WithStreaming: cannot set Stream more than once") + } + g.Stream = cb + return nil + } +} diff --git a/go/plugins/dotprompt/genkit_test.go b/go/plugins/dotprompt/genkit_test.go index 438f35dee..9b439710b 100644 --- a/go/plugins/dotprompt/genkit_test.go +++ b/go/plugins/dotprompt/genkit_test.go @@ -20,12 +20,23 @@ import ( "testing" "github.com/firebase/genkit/go/ai" + "github.com/google/go-cmp/cmp" ) func testGenerate(ctx context.Context, req *ai.ModelRequest, cb func(context.Context, *ai.ModelResponseChunk) error) (*ai.ModelResponse, error) { input := req.Messages[0].Content[0].Text output := fmt.Sprintf("AI reply to %q", input) + if req.Output.Format == "json" { + output = `{"text": "AI reply to JSON"}` + } + + if cb != nil { + cb(ctx, &ai.ModelResponseChunk{ + Content: []*ai.Part{ai.NewTextPart("stream!")}, + }) + } + r := &ai.ModelResponse{ Message: &ai.Message{ Content: []*ai.Part{ @@ -44,26 +55,151 @@ func TestExecute(t *testing.T) { if err != nil { t.Fatal(err) } - resp, err := p.Generate(context.Background(), &PromptRequest{}, nil) + resp, err := p.Generate(context.Background()) if err != nil { t.Fatal(err) } - assertResponse(t, resp) + assertResponse(t, resp, `AI reply to "TestExecute"`) }) t.Run("ModelName", func(t *testing.T) { p, err := New("TestExecute", "TestExecute", Config{ModelName: "test/test"}) if err != nil { t.Fatal(err) } - resp, err := p.Generate(context.Background(), &PromptRequest{}, nil) + resp, err := p.Generate(context.Background()) + if err != nil { + t.Fatal(err) + } + assertResponse(t, resp, `AI reply to "TestExecute"`) + }) + t.Run("GenerateText", func(t *testing.T) { + p, err := New("TestExecute", "TestExecute", Config{ModelName: "test/test"}) + if err != nil { + t.Fatal(err) + } + resp, err := p.GenerateText(context.Background()) + if err != nil { + t.Fatal(err) + } + if resp != `AI reply to "TestExecute"` { + t.Errorf("got %q, want %q", resp, `AI reply to "TestExecute"`) + } + }) + t.Run("GenerateData", func(t *testing.T) { + p, err := New("TestExecute", "TestExecute", Config{ModelName: "test/test"}) + if err != nil { + t.Fatal(err) + } + resp, err := p.GenerateData(context.Background(), InputOutput{}) + if err != nil { + t.Fatal(err) + } + + assertResponse(t, resp, `{"text": "AI reply to JSON"}`) + }) +} + +func TestOptionsPatternGenerate(t *testing.T) { + testModel := ai.DefineModel("options", "test", nil, testGenerate) + + t.Run("Streaming", func(t *testing.T) { + p, err := Define("TestExecute", "TestExecute", WithInputType(InputOutput{})) + if err != nil { + t.Fatal(err) + } + + streamText := "" + resp, err := p.Generate( + context.Background(), + WithInput(InputOutput{ + Text: "testing", + }), + WithStreaming(func(ctx context.Context, grc *ai.ModelResponseChunk) error { + streamText += grc.Text() + return nil + }), + WithModel(testModel), + WithContext([]any{"context"}), + ) if err != nil { t.Fatal(err) } - assertResponse(t, resp) + + assertResponse(t, resp, `AI reply to "TestExecute"`) + if diff := cmp.Diff(streamText, "stream!"); diff != "" { + t.Errorf("Text() diff (+got -want):\n%s", diff) + } }) + + t.Run("WithModelName", func(t *testing.T) { + p, err := Define("TestModelname", "TestModelname", WithInputType(InputOutput{})) + if err != nil { + t.Fatal(err) + } + + resp, err := p.Generate( + context.Background(), + WithInput(InputOutput{ + Text: "testing", + }), + WithModelName("options/test"), + ) + if err != nil { + t.Fatal(err) + } + + assertResponse(t, resp, `AI reply to "TestModelname"`) + }) +} + +func TestGenerateOptions(t *testing.T) { + p, err := Define("TestWithGenerate", "TestWithGenerate", WithInputType(InputOutput{})) + if err != nil { + t.Fatal(err) + } + + var tests = []struct { + name string + with GenerateOption + }{ + { + name: "WithInput", + with: WithInput(map[string]any{"test": "test"}), + }, + { + name: "WithConfig", + with: WithConfig(&ai.GenerationCommonConfig{}), + }, + { + name: "WithContext", + with: WithContext([]any{"context"}), + }, + { + name: "WithModelName", + with: WithModelName("defineoptions/test"), + }, + { + name: "WithModel", + with: WithModel(testModel), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, err = p.Generate( + context.Background(), + test.with, + test.with, + ) + + if err == nil { + t.Errorf("%s could be set twice", test.name) + } + }) + } } -func assertResponse(t *testing.T, resp *ai.ModelResponse) { +func assertResponse(t *testing.T, resp *ai.ModelResponse, want string) { if resp.Message == nil { t.Fatal("response has candidate with no message") } @@ -74,7 +210,6 @@ func assertResponse(t *testing.T, resp *ai.ModelResponse) { } } got := resp.Message.Content[0].Text - want := `AI reply to "TestExecute"` if got != want { t.Errorf("fake model replied with %q, want %q", got, want) } diff --git a/go/plugins/dotprompt/render.go b/go/plugins/dotprompt/render.go index e9b0152b5..98e712e91 100644 --- a/go/plugins/dotprompt/render.go +++ b/go/plugins/dotprompt/render.go @@ -50,9 +50,9 @@ func (p *Prompt) RenderText(variables map[string]any) (string, error) { // RenderMessages executes the prompt's template and converts it into messages. // This just runs the template; it does not call a model. func (p *Prompt) RenderMessages(variables map[string]any) ([]*ai.Message, error) { - if p.VariableDefaults != nil { + if p.DefaultInput != nil { nv := make(map[string]any) - maps.Copy(nv, p.VariableDefaults) + maps.Copy(nv, p.DefaultInput) maps.Copy(nv, variables) variables = nv } diff --git a/go/samples/coffee-shop/main.go b/go/samples/coffee-shop/main.go index 0b0fdc44b..7950a4e3e 100755 --- a/go/samples/coffee-shop/main.go +++ b/go/samples/coffee-shop/main.go @@ -42,7 +42,6 @@ import ( "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/dotprompt" "github.com/firebase/genkit/go/plugins/googleai" - "github.com/invopop/jsonschema" ) const simpleGreetingPromptTemplate = ` @@ -99,17 +98,11 @@ func main() { log.Fatal(err) } - r := &jsonschema.Reflector{ - AllowAdditionalProperties: false, - DoNotReference: true, - } g := googleai.Model("gemini-1.5-pro") simpleGreetingPrompt, err := dotprompt.Define("simpleGreeting2", simpleGreetingPromptTemplate, - dotprompt.Config{ - Model: g, - InputSchema: r.Reflect(simpleGreetingInput{}), - OutputFormat: ai.OutputFormatText, - }, + dotprompt.WithDefaultModel(g), + dotprompt.WithInputType(simpleGreetingInput{}), + dotprompt.WithOutputFormat(ai.OutputFormatText), ) if err != nil { log.Fatal(err) @@ -123,10 +116,8 @@ func main() { } } resp, err := simpleGreetingPrompt.Generate(ctx, - &dotprompt.PromptRequest{ - Variables: input, - }, - callback, + dotprompt.WithInput(input), + dotprompt.WithStreaming(callback), ) if err != nil { return "", err @@ -135,11 +126,9 @@ func main() { }) greetingWithHistoryPrompt, err := dotprompt.Define("greetingWithHistory", greetingWithHistoryPromptTemplate, - dotprompt.Config{ - Model: g, - InputSchema: jsonschema.Reflect(customerTimeAndHistoryInput{}), - OutputFormat: ai.OutputFormatText, - }, + dotprompt.WithDefaultModel(g), + dotprompt.WithInputType(customerTimeAndHistoryInput{}), + dotprompt.WithOutputFormat(ai.OutputFormatText), ) if err != nil { log.Fatal(err) @@ -147,9 +136,7 @@ func main() { greetingWithHistoryFlow := genkit.DefineFlow("greetingWithHistory", func(ctx context.Context, input *customerTimeAndHistoryInput) (string, error) { resp, err := greetingWithHistoryPrompt.Generate(ctx, - &dotprompt.PromptRequest{ - Variables: input, - }, + dotprompt.WithInput(input), nil, ) if err != nil { @@ -159,12 +146,9 @@ func main() { }) simpleStructuredGreetingPrompt, err := dotprompt.Define("simpleStructuredGreeting", simpleStructuredGreetingPromptTemplate, - dotprompt.Config{ - Model: g, - InputSchema: r.Reflect(simpleGreetingInput{}), - OutputFormat: ai.OutputFormatJSON, - OutputSchema: r.Reflect(simpleGreetingOutput{}), - }, + dotprompt.WithDefaultModel(g), + dotprompt.WithInputType(simpleGreetingInput{}), + dotprompt.WithOutputType(simpleGreetingOutput{}), ) if err != nil { log.Fatal(err) @@ -178,10 +162,8 @@ func main() { } } resp, err := simpleStructuredGreetingPrompt.Generate(ctx, - &dotprompt.PromptRequest{ - Variables: input, - }, - callback, + dotprompt.WithInput(input), + dotprompt.WithStreaming(callback), ) if err != nil { return "", err diff --git a/go/samples/menu/s01.go b/go/samples/menu/s01.go index 7d907108f..b9c64813e 100644 --- a/go/samples/menu/s01.go +++ b/go/samples/menu/s01.go @@ -26,10 +26,8 @@ func setup01(ctx context.Context, g ai.Model) error { `You are acting as a helpful AI assistant named "Walt" that can answer questions about the food available on the menu at Walt's Burgers. Customer says: ${input.question}`, - dotprompt.Config{ - Model: g, - InputSchema: menuQuestionInputSchema, - }, + dotprompt.WithDefaultModel(g), + dotprompt.WithInputType(menuQuestionInput{}), ) if err != nil { return err @@ -66,11 +64,9 @@ func setup01(ctx context.Context, g ai.Model) error { Question: {{question}} ?`, - dotprompt.Config{ - Model: g, - InputSchema: menuQuestionInputSchema, - OutputFormat: ai.OutputFormatText, - }, + dotprompt.WithDefaultModel(g), + dotprompt.WithInputType(menuQuestionInput{}), + dotprompt.WithOutputFormat(ai.OutputFormatText), ) return err diff --git a/go/samples/menu/s02.go b/go/samples/menu/s02.go index 63a1c0396..44c73c080 100644 --- a/go/samples/menu/s02.go +++ b/go/samples/menu/s02.go @@ -51,12 +51,9 @@ func setup02(_ context.Context, m ai.Model) error { Question: {{question}} ?`, - dotprompt.Config{ - Model: m, - InputSchema: menuQuestionInputSchema, - OutputFormat: ai.OutputFormatText, - Tools: []ai.Tool{menuTool}, - }, + dotprompt.WithDefaultModel(m), + dotprompt.WithInputType(menuQuestionInput{}), + dotprompt.WithTools(menuTool), ) if err != nil { return err @@ -65,9 +62,7 @@ func setup02(_ context.Context, m ai.Model) error { genkit.DefineFlow("s02_menuQuestion", func(ctx context.Context, input *menuQuestionInput) (*answerOutput, error) { resp, err := dataMenuPrompt.Generate(ctx, - &dotprompt.PromptRequest{ - Variables: input, - }, + dotprompt.WithInput(input), nil, ) if err != nil { diff --git a/go/samples/menu/s03.go b/go/samples/menu/s03.go index 4c277b6c7..7cc1aef9f 100644 --- a/go/samples/menu/s03.go +++ b/go/samples/menu/s03.go @@ -71,14 +71,12 @@ func setup03(ctx context.Context, model ai.Model) error { {{this.description}} {{~/each}} Do you have any questions about the menu?`, - dotprompt.Config{ - Model: model, - InputSchema: dataMenuQuestionInputSchema, - OutputFormat: ai.OutputFormatText, - GenerationConfig: &ai.GenerationCommonConfig{ - Temperature: 0.3, - }, - }, + dotprompt.WithDefaultModel(model), + dotprompt.WithInputType(dataMenuQuestionInput{}), + dotprompt.WithOutputFormat(ai.OutputFormatText), + dotprompt.WithDefaultConfig(&ai.GenerationCommonConfig{ + Temperature: 0.3, + }), ) if err != nil { return err diff --git a/go/samples/menu/s04.go b/go/samples/menu/s04.go index 94cec8fc8..0be3faa81 100644 --- a/go/samples/menu/s04.go +++ b/go/samples/menu/s04.go @@ -40,14 +40,12 @@ func setup04(ctx context.Context, indexer ai.Indexer, retriever ai.Retriever, mo Answer this customer's question: {{question}}?`, - dotprompt.Config{ - Model: model, - InputSchema: dataMenuQuestionInputSchema, - OutputFormat: ai.OutputFormatText, - GenerationConfig: &ai.GenerationCommonConfig{ - Temperature: 0.3, - }, - }, + dotprompt.WithDefaultModel(model), + dotprompt.WithInputType(dataMenuQuestionInput{}), + dotprompt.WithOutputFormat(ai.OutputFormatText), + dotprompt.WithDefaultConfig(&ai.GenerationCommonConfig{ + Temperature: 0.3, + }), ) if err != nil { return err @@ -98,10 +96,10 @@ func setup04(ctx context.Context, indexer ai.Indexer, retriever ai.Retriever, mo Question: input.Question, } - preq := &dotprompt.PromptRequest{ - Variables: questionInput, - } - presp, err := ragDataMenuPrompt.Generate(ctx, preq, nil) + presp, err := ragDataMenuPrompt.Generate( + ctx, + dotprompt.WithInput(questionInput), + nil) if err != nil { return nil, err } diff --git a/go/samples/menu/s05.go b/go/samples/menu/s05.go index 5d8451a02..d561d897b 100644 --- a/go/samples/menu/s05.go +++ b/go/samples/menu/s05.go @@ -22,7 +22,6 @@ import ( "github.com/firebase/genkit/go/ai" "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/dotprompt" - "github.com/invopop/jsonschema" ) type imageURLInput struct { @@ -36,14 +35,12 @@ func setup05(ctx context.Context, gen, genVision ai.Model) error { from the following image of a restaurant menu. {{media url=imageUrl}}`, - dotprompt.Config{ - Model: genVision, - InputSchema: jsonschema.Reflect(imageURLInput{}), - OutputFormat: ai.OutputFormatText, - GenerationConfig: &ai.GenerationCommonConfig{ - Temperature: 0.1, - }, - }, + dotprompt.WithDefaultModel(genVision), + dotprompt.WithInputType(imageURLInput{}), + dotprompt.WithOutputFormat(ai.OutputFormatText), + dotprompt.WithDefaultConfig(&ai.GenerationCommonConfig{ + Temperature: 0.1, + }), ) if err != nil { return err @@ -61,14 +58,12 @@ func setup05(ctx context.Context, gen, genVision ai.Model) error { Answer this customer's question: {{question}}? `, - dotprompt.Config{ - Model: gen, - InputSchema: textMenuQuestionInputSchema, - OutputFormat: ai.OutputFormatText, - GenerationConfig: &ai.GenerationCommonConfig{ - Temperature: 0.3, - }, - }, + dotprompt.WithDefaultModel(gen), + dotprompt.WithInputType(textMenuQuestionInput{}), + dotprompt.WithOutputFormat(ai.OutputFormatText), + dotprompt.WithDefaultConfig(&ai.GenerationCommonConfig{ + Temperature: 0.3, + }), ) if err != nil { return err @@ -87,12 +82,11 @@ func setup05(ctx context.Context, gen, genVision ai.Model) error { data := make([]byte, base64.StdEncoding.EncodedLen(len(image))) base64.StdEncoding.Encode(data, image) imageDataURL := "data:image/jpeg;base64," + string(data) - preq := &dotprompt.PromptRequest{ - Variables: &imageURLInput{ + + presp, err := readMenuPrompt.Generate(ctx, + dotprompt.WithInput(&imageURLInput{ ImageURL: imageDataURL, - }, - } - presp, err := readMenuPrompt.Generate(ctx, preq, nil) + }), nil) if err != nil { return "", err } @@ -107,10 +101,7 @@ func setup05(ctx context.Context, gen, genVision ai.Model) error { textMenuQuestionFlow := genkit.DefineFlow("s05_textMenuQuestion", func(ctx context.Context, input *textMenuQuestionInput) (*answerOutput, error) { - preq := &dotprompt.PromptRequest{ - Variables: input, - } - presp, err := textMenuPrompt.Generate(ctx, preq, nil) + presp, err := textMenuPrompt.Generate(ctx, dotprompt.WithInput(input), nil) if err != nil { return nil, err } diff --git a/go/samples/rag/main.go b/go/samples/rag/main.go index 754548555..3983135e5 100644 --- a/go/samples/rag/main.go +++ b/go/samples/rag/main.go @@ -44,7 +44,6 @@ import ( "github.com/firebase/genkit/go/plugins/dotprompt" "github.com/firebase/genkit/go/plugins/googleai" "github.com/firebase/genkit/go/plugins/localvec" - "github.com/invopop/jsonschema" ) const simpleQaPromptTemplate = ` @@ -83,11 +82,9 @@ func main() { simpleQaPrompt, err := dotprompt.Define("simpleQaPrompt", simpleQaPromptTemplate, - dotprompt.Config{ - Model: model, - InputSchema: jsonschema.Reflect(simpleQaPromptInput{}), - OutputFormat: ai.OutputFormatText, - }, + dotprompt.WithDefaultModel(model), + dotprompt.WithInputType(simpleQaPromptInput{}), + dotprompt.WithOutputFormat(ai.OutputFormatText), ) if err != nil { log.Fatal(err) @@ -121,9 +118,7 @@ func main() { } resp, err := simpleQaPrompt.Generate(ctx, - &dotprompt.PromptRequest{ - Variables: promptInput, - }, + dotprompt.WithInput(promptInput), nil, ) if err != nil { From 7255cd67fa32bb41e7ca2ba1d632098cad2b6e8b Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:18:53 -0500 Subject: [PATCH 064/562] feat(evals): New data format for evals IO (#1473) --- docs/evaluation.md | 31 ++++++++------ .../cli/src/commands/eval-extract-data.ts | 8 ++-- genkit-tools/cli/src/commands/eval-flow.ts | 5 +-- .../cli/src/commands/flow-batch-run.ts | 11 +++-- genkit-tools/common/src/eval/evaluate.ts | 17 +++----- .../common/src/eval/localFileDatasetStore.ts | 16 ++------ genkit-tools/common/src/types/eval.ts | 28 ++++--------- .../tests/eval/localFileDatasetStore_test.ts | 40 +++++++++---------- .../evals/data/cat_adoption_questions.json | 16 ++++++-- ...cat_adoption_questions_with_reference.json | 38 +++++++++--------- 10 files changed, 97 insertions(+), 113 deletions(-) diff --git a/docs/evaluation.md b/docs/evaluation.md index b27918265..5102ec901 100644 --- a/docs/evaluation.md +++ b/docs/evaluation.md @@ -46,25 +46,30 @@ called `testInputs.json`. This input dataset represents the test cases you will use to generate output for evaluation. ```json -["Cheese", "Broccoli", "Spinach and Kale"] +[ + { + "input": "What is the French word for Cheese?" + }, + { + "input": "What green vegetable looks like cauliflower?" + } +] ``` If the evaluator requires a reference output for evaluating a flow, you can pass both input and reference output using this format instead: ```json -{ - "samples": [ - { - "input": "What is the French word for Cheese?", - "reference": "Fromage" - }, - { - "input": "What green vegetable looks like cauliflower?", - "reference": "Broccoli" - } - ] -} +[ + { + "input": "What is the French word for Cheese?", + "reference": "Fromage" + }, + { + "input": "What green vegetable looks like cauliflower?", + "reference": "Broccoli" + } +] ``` Note that you can use any JSON data type in the input JSON file. Genkit will pass them along with the same data type to your flow. diff --git a/genkit-tools/cli/src/commands/eval-extract-data.ts b/genkit-tools/cli/src/commands/eval-extract-data.ts index 3679091a6..e90c6ff55 100644 --- a/genkit-tools/cli/src/commands/eval-extract-data.ts +++ b/genkit-tools/cli/src/commands/eval-extract-data.ts @@ -58,12 +58,10 @@ export const evalExtractData = new Command('eval:extractData') .map((t) => { const rootSpan = Object.values(t.spans).find( (s) => - s.attributes['genkit:type'] === 'flow' && + s.attributes['genkit:metadata:subtype'] === 'flow' && (!options.label || - s.attributes['genkit:metadata:flow:label:batchRun'] === - options.label) && - s.attributes['genkit:metadata:flow:name'] === flowName && - s.attributes['genkit:metadata:flow:state'] === 'done' + s.attributes['batchRun'] === options.label) && + s.attributes['genkit:name'] === flowName ); if (!rootSpan) { return undefined; diff --git a/genkit-tools/cli/src/commands/eval-flow.ts b/genkit-tools/cli/src/commands/eval-flow.ts index 349522fcb..9bf563e65 100644 --- a/genkit-tools/cli/src/commands/eval-flow.ts +++ b/genkit-tools/cli/src/commands/eval-flow.ts @@ -42,7 +42,7 @@ interface EvalFlowRunCliOptions { outputFormat: string; } -const EVAL_FLOW_SCHEMA = '{samples: Array<{input: any; reference?: any;}>}'; +const EVAL_FLOW_SCHEMA = 'Array<{input: any; reference?: any;}>'; enum SourceType { DATA = 'data', FILE = 'file', @@ -177,8 +177,7 @@ async function readInputs( case SourceType.DATASET: const datasetStore = await getDatasetStore(); const data = await datasetStore.getDataset(input!); - // Format to match EvalInferenceInputSchema - parsedData = { samples: data }; + parsedData = data; break; } diff --git a/genkit-tools/cli/src/commands/flow-batch-run.ts b/genkit-tools/cli/src/commands/flow-batch-run.ts index a739cec46..269040806 100644 --- a/genkit-tools/cli/src/commands/flow-batch-run.ts +++ b/genkit-tools/cli/src/commands/flow-batch-run.ts @@ -49,12 +49,17 @@ export const flowBatchRun = new Command('flow:batchRun') ) => { await runWithManager(async (manager) => { const inputData = JSON.parse(await readFile(fileName, 'utf8')) as any[]; - if (!Array.isArray(inputData)) { - throw new Error('batch input data must be an array'); + let input = inputData; + if (inputData.length === 0) { + throw new Error('batch input data must be a non-empty array'); + } + if (Object.hasOwn(inputData[0], 'input')) { + // If object has "input" field, use that instead. + input = inputData.map((d) => d.input); } const outputValues = [] as { input: any; output: any }[]; - for (const data of inputData) { + for (const data of input) { logger.info(`Running '/flow/${flowName}'...`); let response = await manager.runAction({ key: `/flow/${flowName}`, diff --git a/genkit-tools/common/src/eval/evaluate.ts b/genkit-tools/common/src/eval/evaluate.ts index 22b17edf0..09f01b913 100644 --- a/genkit-tools/common/src/eval/evaluate.ts +++ b/genkit-tools/common/src/eval/evaluate.ts @@ -84,7 +84,7 @@ export async function runNewEvaluation( const evalDataset = await runInference({ manager, actionRef, - evalFlowInput: EvalInferenceInputSchema.parse({ samples: dataset }), + evalFlowInput: EvalInferenceInputSchema.parse(dataset), auth: request.options?.auth, actionConfig: request.options?.actionConfig, }); @@ -216,16 +216,11 @@ async function bulkRunAction(params: { }): Promise { const { manager, actionRef, evalFlowInput, auth, actionConfig } = params; const isModelAction = actionRef.startsWith('/model'); - let testCases: TestCase[] = Array.isArray(evalFlowInput) - ? (evalFlowInput as any[]).map((i) => ({ - input: i, - testCaseId: generateTestCaseId(), - })) - : evalFlowInput.samples.map((c) => ({ - input: c.input, - reference: c.reference, - testCaseId: c.testCaseId ?? generateTestCaseId(), - })); + let testCases: TestCase[] = evalFlowInput.map((c) => ({ + input: c.input, + reference: c.reference, + testCaseId: c.testCaseId ?? generateTestCaseId(), + })); if (testCases.length === 0) { throw new Error('Cannot run inference, no data provided'); } diff --git a/genkit-tools/common/src/eval/localFileDatasetStore.ts b/genkit-tools/common/src/eval/localFileDatasetStore.ts index 4f96cfd25..4263b369d 100644 --- a/genkit-tools/common/src/eval/localFileDatasetStore.ts +++ b/genkit-tools/common/src/eval/localFileDatasetStore.ts @@ -241,18 +241,10 @@ export class LocalFileDatasetStore implements DatasetStore { } private getDatasetFromInferenceInput(data: EvalInferenceInput): Dataset { - if (Array.isArray(data)) { - return data.map((d) => ({ - testCaseId: d.testCaseId ?? generateTestCaseId(), - input: d, - })); - } else if (!!data.samples) { - return data.samples.map((d) => ({ - testCaseId: d.testCaseId ?? generateTestCaseId(), - ...d, - })); - } - return []; + return data.map((d) => ({ + testCaseId: d.testCaseId ?? generateTestCaseId(), + ...d, + })); } private async patchDataset( diff --git a/genkit-tools/common/src/types/eval.ts b/genkit-tools/common/src/types/eval.ts index 120769f97..b83b58a43 100644 --- a/genkit-tools/common/src/types/eval.ts +++ b/genkit-tools/common/src/types/eval.ts @@ -29,22 +29,6 @@ import { GenerateRequestSchema } from './model'; * This file defines schema and types that are used by the Eval store. */ -/** - * Structured input for inference part of evaluation - */ -export const EvalInferenceStructuredInputSchema = z.object({ - samples: z.array( - z.object({ - testCaseId: z.string().optional(), - input: z.any(), - reference: z.any().optional(), - }) - ), -}); -export type EvalInferenceStructuredInput = z.infer< - typeof EvalInferenceStructuredInputSchema ->; - /** * Supported datatype when running eval-inference using models */ @@ -60,15 +44,19 @@ export const ModelInferenceInputJSONSchema = zodToJsonSchema( removeAdditionalStrategy: 'strict', } ) as JSONSchema7; + /** * A set of samples that is ready for inference. * * This should be used in user-facing surfaces (CLI/API inputs) to accommodate various user input formats. For internal wire-transfer/storage, prefer {@link Dataset}. */ -export const EvalInferenceInputSchema = z.union([ - z.array(z.any()), - EvalInferenceStructuredInputSchema, -]); +export const EvalInferenceInputSchema = z.array( + z.object({ + testCaseId: z.string().optional(), + input: z.any(), + reference: z.any().optional(), + }) +); export type EvalInferenceInput = z.infer; /** diff --git a/genkit-tools/common/tests/eval/localFileDatasetStore_test.ts b/genkit-tools/common/tests/eval/localFileDatasetStore_test.ts index 3aa8e6df0..17e811611 100644 --- a/genkit-tools/common/tests/eval/localFileDatasetStore_test.ts +++ b/genkit-tools/common/tests/eval/localFileDatasetStore_test.ts @@ -88,12 +88,12 @@ const SAMPLE_DATASET_METADATA_1_V2 = { }; const CREATE_DATASET_REQUEST = CreateDatasetRequestSchema.parse({ - data: { samples: SAMPLE_DATASET_1_V1 }, + data: SAMPLE_DATASET_1_V1, datasetType: 'UNKNOWN', }); const CREATE_DATASET_REQUEST_WITH_SCHEMA = CreateDatasetRequestSchema.parse({ - data: { samples: SAMPLE_DATASET_1_V1 }, + data: SAMPLE_DATASET_1_V1, datasetType: 'UNKNOWN', schema: { inputSchema: { @@ -109,7 +109,7 @@ const CREATE_DATASET_REQUEST_WITH_SCHEMA = CreateDatasetRequestSchema.parse({ }); const UPDATE_DATASET_REQUEST = UpdateDatasetRequestSchema.parse({ - data: { samples: SAMPLE_DATASET_1_V2 }, + data: SAMPLE_DATASET_1_V2, datasetId: SAMPLE_DATASET_ID_1, }); @@ -205,7 +205,7 @@ describe('localFileDatasetStore', () => { const datasetMetadata = await DatasetStore.createDataset({ ...CREATE_DATASET_REQUEST, - data: { samples: dataset }, + data: dataset, datasetId: SAMPLE_DATASET_ID_1, }); @@ -304,13 +304,11 @@ describe('localFileDatasetStore', () => { .mockImplementation(() => Promise.resolve(dataset)); const datasetMetadata = await DatasetStore.updateDataset({ - data: { - samples: [ - { - input: 'A new information on cat dog', - }, - ], - }, + data: [ + { + input: 'A new information on cat dog', + }, + ], datasetId: SAMPLE_DATASET_ID_1, }); @@ -357,17 +355,15 @@ describe('localFileDatasetStore', () => { .mockImplementation(() => Promise.resolve(dataset)); const datasetMetadata = await DatasetStore.updateDataset({ - data: { - samples: [ - { - input: 'A new information on cat dog', - }, - { - testCaseId: '1', - input: 'Other information on hot dog', - }, - ], - }, + data: [ + { + input: 'A new information on cat dog', + }, + { + testCaseId: '1', + input: 'Other information on hot dog', + }, + ], datasetId: SAMPLE_DATASET_ID_1, }); diff --git a/js/testapps/evals/data/cat_adoption_questions.json b/js/testapps/evals/data/cat_adoption_questions.json index d4731960b..e9d37bb59 100644 --- a/js/testapps/evals/data/cat_adoption_questions.json +++ b/js/testapps/evals/data/cat_adoption_questions.json @@ -1,6 +1,14 @@ [ - "What are typical cat behaviors?", - "What supplies do you need when bringing home a new cat?", - "How often should you trim your cat's nails?", - "What are some plants that are toxic to cats?" + { + "input": "What are typical cat behaviors?" + }, + { + "input": "What supplies do you need when bringing home a new cat?" + }, + { + "input": "How often should you trim your cat's nails?" + }, + { + "input": "What are some plants that are toxic to cats?" + } ] diff --git a/js/testapps/evals/data/cat_adoption_questions_with_reference.json b/js/testapps/evals/data/cat_adoption_questions_with_reference.json index 2ff409b66..3e1dde61f 100644 --- a/js/testapps/evals/data/cat_adoption_questions_with_reference.json +++ b/js/testapps/evals/data/cat_adoption_questions_with_reference.json @@ -1,20 +1,18 @@ -{ - "samples": [ - { - "input": "What are typical cat behaviors?", - "reference": "Cats like to purr, push things away and cuddle." - }, - { - "input": "What supplies do you need when bringing home a new cat?", - "reference": "Litter box, cat food and plenty of yarn" - }, - { - "input": "How often should you trim your cat's nails?", - "reference": "Trim your cat's nails only when you feel like they're overgrown" - }, - { - "input": "What are some plants that are toxic to cats?", - "reference": "I don't know, maybe poison ivy?" - } - ] -} +[ + { + "input": "What are typical cat behaviors?", + "reference": "Cats like to purr, push things away and cuddle." + }, + { + "input": "What supplies do you need when bringing home a new cat?", + "reference": "Litter box, cat food and plenty of yarn" + }, + { + "input": "How often should you trim your cat's nails?", + "reference": "Trim your cat's nails only when you feel like they're overgrown" + }, + { + "input": "What are some plants that are toxic to cats?", + "reference": "I don't know, maybe poison ivy?" + } +] From afcd77b17cc5e54a146df62e2d7ac96efed87302 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Wed, 11 Dec 2024 17:14:25 -0500 Subject: [PATCH 065/562] fix(js/core): correctly handle errors when streaming (#1477) --- genkit-tools/common/src/manager/manager.ts | 24 ++++++++-- genkit-tools/common/src/manager/types.ts | 4 +- genkit-tools/common/src/server/router.ts | 6 +-- genkit-tools/common/src/server/server.ts | 16 +++++-- genkit-tools/common/src/types/error.ts | 28 +++++++++++ genkit-tools/common/src/types/index.ts | 1 + js/core/src/action.ts | 19 +++++--- js/core/src/reflection.ts | 55 +++++++++++++++------- js/testapps/flow-simple-ai/src/index.ts | 31 +++++++++++- 9 files changed, 146 insertions(+), 38 deletions(-) create mode 100644 genkit-tools/common/src/types/error.ts diff --git a/genkit-tools/common/src/manager/manager.ts b/genkit-tools/common/src/manager/manager.ts index 1f4d15109..bf466c914 100644 --- a/genkit-tools/common/src/manager/manager.ts +++ b/genkit-tools/common/src/manager/manager.ts @@ -25,6 +25,7 @@ import { RunActionResponseSchema, } from '../types/action'; import * as apis from '../types/apis'; +import { GenkitErrorData } from '../types/error'; import { TraceData } from '../types/trace'; import { logger } from '../utils/logger'; import { @@ -198,9 +199,24 @@ export class RuntimeManager { rejecter = reject; }); stream.on('end', () => { - const actionResponse = RunActionResponseSchema.parse( - JSON.parse(buffer) - ); + const parsedBuffer = JSON.parse(buffer); + if (parsedBuffer.error) { + const err = new GenkitToolsError( + `Error running action key='${input.key}'.` + ); + // massage the error into a shape dev ui expects + err.data = { + ...parsedBuffer.error, + stack: (parsedBuffer.error?.details as any).stack, + data: { + genkitErrorMessage: parsedBuffer.error?.message, + genkitErrorDetails: parsedBuffer.error?.details, + }, + }; + rejecter(err); + return; + } + const actionResponse = RunActionResponseSchema.parse(parsedBuffer); if (genkitVersion) { actionResponse.genkitVersion = genkitVersion; } @@ -392,7 +408,7 @@ export class RuntimeManager { newError.message = (error.response?.data as any).message; } // we got a non-200 response; copy the payload and rethrow - newError.data = error.response.data as Record; + newError.data = error.response.data as GenkitErrorData; throw newError; } diff --git a/genkit-tools/common/src/manager/types.ts b/genkit-tools/common/src/manager/types.ts index 3fb9b0325..7d9fd2fa5 100644 --- a/genkit-tools/common/src/manager/types.ts +++ b/genkit-tools/common/src/manager/types.ts @@ -14,10 +14,12 @@ * limitations under the License. */ +import { GenkitErrorData } from '../types/error'; + export type Runtime = 'nodejs' | 'go' | undefined; export class GenkitToolsError extends Error { - public data?: Record; + public data?: GenkitErrorData; constructor(msg: string, options?: ErrorOptions) { super(msg, options); diff --git a/genkit-tools/common/src/server/router.ts b/genkit-tools/common/src/server/router.ts index 85058f6d4..dda959011 100644 --- a/genkit-tools/common/src/server/router.ts +++ b/genkit-tools/common/src/server/router.ts @@ -35,10 +35,8 @@ const t = initTRPC.create({ ...shape, data: { ...shape.data, - genkitErrorMessage: (error.cause.data as Record) - .message, - genkitErrorDetails: (error.cause.data as Record) - .details, + genkitErrorMessage: error.cause.data.message, + genkitErrorDetails: error.cause.data.details, }, }; } diff --git a/genkit-tools/common/src/server/server.ts b/genkit-tools/common/src/server/server.ts index 6a2da6b25..100ca061e 100644 --- a/genkit-tools/common/src/server/server.ts +++ b/genkit-tools/common/src/server/server.ts @@ -21,6 +21,7 @@ import express, { ErrorRequestHandler } from 'express'; import { Server } from 'http'; import os from 'os'; import path from 'path'; +import { GenkitToolsError } from '../manager'; import { RuntimeManager } from '../manager/manager'; import { logger } from '../utils/logger'; import { toolsPackage } from '../utils/package'; @@ -72,10 +73,17 @@ export function startServer(manager: RuntimeManager, port: number) { 'Transfer-Encoding': 'chunked', }); - const result = await manager.runAction({ key, input, context }, (chunk) => { - res.write(JSON.stringify(chunk) + '\n'); - }); - res.write(JSON.stringify(result)); + try { + const result = await manager.runAction( + { key, input, context }, + (chunk) => { + res.write(JSON.stringify(chunk) + '\n'); + } + ); + res.write(JSON.stringify(result)); + } catch (err) { + res.write(JSON.stringify({ error: (err as GenkitToolsError).data })); + } res.end(); }); diff --git a/genkit-tools/common/src/types/error.ts b/genkit-tools/common/src/types/error.ts new file mode 100644 index 000000000..e67e1cff3 --- /dev/null +++ b/genkit-tools/common/src/types/error.ts @@ -0,0 +1,28 @@ +/** + * Copyright 2024 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. + */ + +export interface GenkitErrorData { + message: string; + stack?: string; + details?: any; + data?: { + genkitErrorMessage?: string; + genkitErrorDetails?: { + stack?: string; + traceId: string; + }; + }; +} diff --git a/genkit-tools/common/src/types/index.ts b/genkit-tools/common/src/types/index.ts index 0ddcdc1ee..d2678a44d 100644 --- a/genkit-tools/common/src/types/index.ts +++ b/genkit-tools/common/src/types/index.ts @@ -15,6 +15,7 @@ */ export { RuntimeEvent, RuntimeInfo } from '../manager/types'; +export { GenkitErrorData } from '../types/error'; export * from './action'; export * from './analytics'; export * from './apis'; diff --git a/js/core/src/action.ts b/js/core/src/action.ts index fe18083a5..3428370e3 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -271,13 +271,20 @@ export function action< metadata.name = actionName; metadata.input = input; - const output = await fn(input, { - context: options?.context, - sendChunk: options?.onChunk ?? ((c) => {}), - }); + try { + const output = await fn(input, { + context: options?.context, + sendChunk: options?.onChunk ?? ((c) => {}), + }); - metadata.output = JSON.stringify(output); - return output; + metadata.output = JSON.stringify(output); + return output; + } catch (err) { + if (typeof err === 'object') { + (err as any).traceId = traceId; + } + throw err; + } } ); output = parseSchema(output, { diff --git a/js/core/src/reflection.ts b/js/core/src/reflection.ts index dae0054e1..3201e15e5 100644 --- a/js/core/src/reflection.ts +++ b/js/core/src/reflection.ts @@ -156,7 +156,6 @@ export class ReflectionServer { const { key, input, context, telemetryLabels } = request.body; const { stream } = request.query; logger.debug(`Running action \`${key}\` with stream=${stream}...`); - let traceId; try { const action = await this.registry.lookupAction(key); if (!action) { @@ -164,23 +163,43 @@ export class ReflectionServer { return; } if (stream === 'true') { - const callback = (chunk) => { - response.write(JSON.stringify(chunk) + '\n'); - }; - const result = await runWithStreamingCallback( - callback, - async () => await action.run(input, { context, onChunk: callback }) - ); - await flushTracing(); - response.write( - JSON.stringify({ - result: result.result, - telemetry: { - traceId: result.telemetry.traceId, + try { + const callback = (chunk) => { + response.write(JSON.stringify(chunk) + '\n'); + }; + const result = await runWithStreamingCallback(callback, () => + action.run(input, { context, onChunk: callback }) + ); + await flushTracing(); + response.write( + JSON.stringify({ + result: result.result, + telemetry: { + traceId: result.telemetry.traceId, + }, + } as RunActionResponse) + ); + response.end(); + } catch (err) { + const { message, stack } = err as Error; + // since we're streaming, we must do special error handling here -- the headers are already sent. + const errorResponse: Status = { + code: StatusCodes.INTERNAL, + message, + details: { + stack, }, - } as RunActionResponse) - ); - response.end(); + }; + if ((err as any).traceId) { + errorResponse.details.traceId = (err as any).traceId; + } + response.write( + JSON.stringify({ + error: errorResponse, + } as RunActionResponse) + ); + response.end(); + } } else { const result = await action.run(input, { context, telemetryLabels }); await flushTracing(); @@ -192,7 +211,7 @@ export class ReflectionServer { } as RunActionResponse); } } catch (err) { - const { message, stack } = err as Error; + const { message, stack, traceId } = err as any; next({ message, stack, traceId }); } }); diff --git a/js/testapps/flow-simple-ai/src/index.ts b/js/testapps/flow-simple-ai/src/index.ts index ecc27cfe7..8231c82b2 100644 --- a/js/testapps/flow-simple-ai/src/index.ts +++ b/js/testapps/flow-simple-ai/src/index.ts @@ -26,8 +26,9 @@ import { GoogleAIFileManager } from '@google/generative-ai/server'; import { AlwaysOnSampler } from '@opentelemetry/sdk-trace-base'; import { initializeApp } from 'firebase-admin/app'; import { getFirestore } from 'firebase-admin/firestore'; -import { MessageSchema, genkit, run, z } from 'genkit'; +import { GenerateResponseData, MessageSchema, genkit, run, z } from 'genkit'; import { logger } from 'genkit/logging'; +import { ModelMiddleware } from 'genkit/model'; import { PluginProvider } from 'genkit/plugin'; import { Allow, parse } from 'partial-json'; @@ -580,3 +581,31 @@ ai.defineFlow( return text; } ); + +ai.defineModel( + { + name: 'hiModel', + }, + async (request, streamingCallback) => { + return { + finishReason: 'stop', + message: { role: 'model', content: [{ text: 'hi' }] }, + }; + } +); + +const blockingMiddleware: ModelMiddleware = async (req, next) => { + return { + finishReason: 'blocked', + finishMessage: `Model input violated policies: further processing blocked.`, + } as GenerateResponseData; +}; + +ai.defineFlow('blockingMiddleware', async () => { + const { text } = await ai.generate({ + prompt: 'hi', + model: 'hiModel', + use: [blockingMiddleware], + }); + return text; +}); From a265ec13854df858955a3aefc77329c833805d26 Mon Sep 17 00:00:00 2001 From: Hunter Heston Date: Wed, 11 Dec 2024 14:40:38 -0800 Subject: [PATCH 066/562] feat(js/plugins/checks): checks evaluator plugin returns multiple scores (#1370) --- .gitignore | 1 + js/plugins/checks/README.md | 11 ++------ js/plugins/checks/src/evaluation.ts | 43 +++++++++++++++-------------- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index d53fc1823..46736ee1c 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ js/testapps/firebase-functions-sample1/.firebase js/testapps/firebase-functions-sample1/.firebaserc js/testapps/firebase-functions-sample1/public/bundle.js js/testapps/firebase-functions-sample1/public/config.js +.genkit js/**/.genkit samples/**/.genkit go/**/.genkit diff --git a/js/plugins/checks/README.md b/js/plugins/checks/README.md index 5cf804438..9b26f2fab 100644 --- a/js/plugins/checks/README.md +++ b/js/plugins/checks/README.md @@ -78,16 +78,11 @@ Create a JSON file with the data you want to test. Add as many test cases as you ``` -### Run the evaluators +### Run the evaluator ```bash -# Run just the DANGEROUS_CONTENT classifier. -genkit eval:run test-dataset.json --evaluators=checks/dangerous_content -``` - -```bash -# Run all classifiers. -genkit eval:run test-dataset.json --evaluators=checks/dangerous_content,checks/pii_soliciting_reciting,checks/harassment,checks/sexually_explicit,checks/hate_speech,checks/medical_info,checks/violence_and_gore,checks/obscenity_and_profanity +# Run all configured classifiers. +genkit eval:run test-dataset.json --evaluators=checks/guardrails ``` ### View the results diff --git a/js/plugins/checks/src/evaluation.ts b/js/plugins/checks/src/evaluation.ts index 58ad2b115..a3eb9afcc 100644 --- a/js/plugins/checks/src/evaluation.ts +++ b/js/plugins/checks/src/evaluation.ts @@ -64,7 +64,7 @@ export function checksEvaluators( auth: GoogleAuth, metrics: ChecksEvaluationMetric[], projectId: string -): EvaluatorAction[] { +): EvaluatorAction { const policy_configs: ChecksEvaluationMetricConfig[] = metrics.map( (metric) => { const metricType = isConfig(metric) ? metric.type : metric; @@ -77,11 +77,7 @@ export function checksEvaluators( } ); - const evaluators = policy_configs.map((policy_config) => { - return createPolicyEvaluator(projectId, auth, ai, policy_config); - }); - - return evaluators; + return createPolicyEvaluator(projectId, auth, ai, policy_configs); } function isConfig( @@ -104,15 +100,13 @@ function createPolicyEvaluator( projectId: string, auth: GoogleAuth, ai: Genkit, - policy_config: ChecksEvaluationMetricConfig + policy_config: ChecksEvaluationMetricConfig[] ): EvaluatorAction { - const policyType = policy_config.type as string; - return ai.defineEvaluator( { - name: `checks/${policyType.toLowerCase()}`, - displayName: policyType, - definition: `Evaluates text against the Checks ${policyType} policy.`, + name: 'checks/guardrails', + displayName: 'checks/guardrails', + definition: `Evaluates input text against the Checks ${policy_config.map((policy) => policy.type)} policies.`, }, async (datapoint: BaseEvalDataPoint) => { const partialRequest = { @@ -121,10 +115,12 @@ function createPolicyEvaluator( content: datapoint.output as string, }, }, - policies: { - policy_type: policy_config.type, - threshold: policy_config.threshold, - }, + policies: policy_config.map((config) => { + return { + policy_type: config.type, + threshold: config.threshold, + }; + }), }; const response = await checksEvalInstance( @@ -134,13 +130,18 @@ function createPolicyEvaluator( ResponseSchema ); - return { - evaluation: { - score: response.policyResults[0].score, + const evaluationResults = response.policyResults.map((result) => { + return { + id: result.policyType, + score: result.score, details: { - reasoning: response.policyResults[0].violationResult, + reasoning: `Status ${result.violationResult}`, }, - }, + }; + }); + + return { + evaluation: evaluationResults, testCaseId: datapoint.testCaseId, }; } From 6fbe98e16e7df9465d28653889106e32b586c02a Mon Sep 17 00:00:00 2001 From: Iman Rahmatizadeh <678917+i14h@users.noreply.github.com> Date: Thu, 12 Dec 2024 07:11:41 +0400 Subject: [PATCH 067/562] docs: Update README.md (#1431) Deprecating Discussions in favor of Discord --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 956e1aecc..9ce658b5c 100644 --- a/README.md +++ b/README.md @@ -113,8 +113,6 @@ Take a look at some samples of Genkit in use: - **Provide feedback:** Report issues or suggest new features using our GitHub [issue tracker](https://github.com/firebase/genkit/issues). -- **Engage in discussions:** Participate in conversations about Genkit on our [GitHub Discussions](https://github.com/firebase/genkit/discussions) forum. - ## Contributing Contributions to Genkit are welcome and highly appreciated! See our [Contribution Guide](CONTRIBUTING.md) to get started. From 540d3b721a1aa4d5f609c94f925efd1d08f11bb4 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Wed, 11 Dec 2024 22:47:13 -0500 Subject: [PATCH 068/562] refactor(js/core): manage als instances centrally in the registry (#1490) --- js/ai/src/chat.ts | 157 ++++++++++-------- js/ai/src/evaluator.ts | 1 + js/ai/src/generate.ts | 1 + js/ai/src/generate/action.ts | 4 +- js/ai/src/model.ts | 2 +- js/ai/src/session.ts | 16 +- js/ai/src/testing/model-tester.ts | 72 ++++---- js/ai/src/tool.ts | 5 +- js/core/src/action.ts | 52 ++++-- js/core/src/auth.ts | 31 +++- js/core/src/flow.ts | 59 ++++++- js/core/src/plugin.ts | 8 +- js/core/src/reflection.ts | 6 +- js/core/src/registry.ts | 28 ++++ js/core/src/tracing/instrumentation.ts | 79 ++++++--- js/core/tests/action_test.ts | 14 +- js/core/tests/registry_test.ts | 36 +++- js/genkit/src/genkit.ts | 5 +- js/genkit/src/tracing.ts | 2 - js/plugins/checks/src/evaluation.ts | 3 + js/plugins/dotprompt/src/prompt.ts | 11 +- js/plugins/express/src/index.ts | 4 +- .../src/evaluation/evaluator_factory.ts | 3 + js/pnpm-lock.yaml | 53 +++--- js/testapps/rag/package.json | 3 +- 25 files changed, 425 insertions(+), 230 deletions(-) diff --git a/js/ai/src/chat.ts b/js/ai/src/chat.ts index 437e9887e..c565b0045 100644 --- a/js/ai/src/chat.ts +++ b/js/ai/src/chat.ts @@ -133,43 +133,47 @@ export class Chat { >( options: string | Part[] | ChatGenerateOptions ): Promise>> { - return runWithSession(this.session, () => - runInNewSpan({ metadata: { name: 'send' } }, async () => { - let resolvedOptions; - let streamingCallback = undefined; - - // string - if (typeof options === 'string') { - resolvedOptions = { - prompt: options, - } as ChatGenerateOptions; - } else if (Array.isArray(options)) { - // Part[] - resolvedOptions = { - prompt: options, - } as ChatGenerateOptions; - } else { - resolvedOptions = options as ChatGenerateOptions; - streamingCallback = resolvedOptions.streamingCallback; + return runWithSession(this.session.registry, this.session, () => + runInNewSpan( + this.session.registry, + { metadata: { name: 'send' } }, + async () => { + let resolvedOptions; + let streamingCallback = undefined; + + // string + if (typeof options === 'string') { + resolvedOptions = { + prompt: options, + } as ChatGenerateOptions; + } else if (Array.isArray(options)) { + // Part[] + resolvedOptions = { + prompt: options, + } as ChatGenerateOptions; + } else { + resolvedOptions = options as ChatGenerateOptions; + streamingCallback = resolvedOptions.streamingCallback; + } + let request: GenerateOptions = { + ...(await this.requestBase), + messages: this.messages, + ...resolvedOptions, + }; + let response = await generate(this.session.registry, { + ...request, + streamingCallback, + }); + this.requestBase = Promise.resolve({ + ...(await this.requestBase), + // these things may get changed by tools calling within generate. + tools: response?.request?.tools, + config: response?.request?.config, + }); + await this.updateMessages(response.messages); + return response; } - let request: GenerateOptions = { - ...(await this.requestBase), - messages: this.messages, - ...resolvedOptions, - }; - let response = await generate(this.session.registry, { - ...request, - streamingCallback, - }); - this.requestBase = Promise.resolve({ - ...(await this.requestBase), - // these things may get changed by tools calling within generate. - tools: response?.request?.tools, - config: response?.request?.config, - }); - await this.updateMessages(response.messages); - return response; - }) + ) ); } @@ -179,47 +183,54 @@ export class Chat { >( options: string | Part[] | GenerateStreamOptions ): Promise>> { - return runWithSession(this.session, () => - runInNewSpan({ metadata: { name: 'send' } }, async () => { - let resolvedOptions; - - // string - if (typeof options === 'string') { - resolvedOptions = { - prompt: options, - } as GenerateStreamOptions; - } else if (Array.isArray(options)) { - // Part[] - resolvedOptions = { - prompt: options, - } as GenerateStreamOptions; - } else { - resolvedOptions = options as GenerateStreamOptions; - } + return runWithSession(this.session.registry, this.session, () => + runInNewSpan( + this.session.registry, + { metadata: { name: 'send' } }, + async () => { + let resolvedOptions; - const { response, stream } = await generateStream( - this.session.registry, - { - ...(await this.requestBase), - messages: this.messages, - ...resolvedOptions, + // string + if (typeof options === 'string') { + resolvedOptions = { + prompt: options, + } as GenerateStreamOptions; + } else if (Array.isArray(options)) { + // Part[] + resolvedOptions = { + prompt: options, + } as GenerateStreamOptions; + } else { + resolvedOptions = options as GenerateStreamOptions< + O, + CustomOptions + >; } - ); - return { - response: response.finally(async () => { - const resolvedResponse = await response; - this.requestBase = Promise.resolve({ + const { response, stream } = await generateStream( + this.session.registry, + { ...(await this.requestBase), - // these things may get changed by tools calling within generate. - tools: resolvedResponse?.request?.tools, - config: resolvedResponse?.request?.config, - }); - this.updateMessages(resolvedResponse.messages); - }), - stream, - }; - }) + messages: this.messages, + ...resolvedOptions, + } + ); + + return { + response: response.finally(async () => { + const resolvedResponse = await response; + this.requestBase = Promise.resolve({ + ...(await this.requestBase), + // these things may get changed by tools calling within generate. + tools: resolvedResponse?.request?.tools, + config: resolvedResponse?.request?.config, + }); + this.updateMessages(resolvedResponse.messages); + }), + stream, + }; + } + ) ); } diff --git a/js/ai/src/evaluator.ts b/js/ai/src/evaluator.ts index 65b75e3e4..64ef67434 100644 --- a/js/ai/src/evaluator.ts +++ b/js/ai/src/evaluator.ts @@ -175,6 +175,7 @@ export function defineEvaluator< }; try { await runInNewSpan( + registry, { metadata: { name: `Test Case ${datapoint.testCaseId}`, diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index e6d1bb2af..1478405bd 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -278,6 +278,7 @@ export async function generate< }; return await runWithStreamingCallback( + registry, resolvedOptions.streamingCallback, async () => { const response = await generateHelper( diff --git a/js/ai/src/generate/action.ts b/js/ai/src/generate/action.ts index 48d6b6e9b..bea20b728 100644 --- a/js/ai/src/generate/action.ts +++ b/js/ai/src/generate/action.ts @@ -83,6 +83,7 @@ export async function generateHelper( ): Promise { // do tracing return await runInNewSpan( + registry, { metadata: { name: 'generate', @@ -139,8 +140,9 @@ async function generate( const accumulatedChunks: GenerateResponseChunkData[] = []; - const streamingCallback = getStreamingCallback(); + const streamingCallback = getStreamingCallback(registry); const response = await runWithStreamingCallback( + registry, streamingCallback ? (chunk: GenerateResponseChunkData) => { // Store accumulated chunk data diff --git a/js/ai/src/model.ts b/js/ai/src/model.ts index c0eb0b9c8..a1693b8ec 100644 --- a/js/ai/src/model.ts +++ b/js/ai/src/model.ts @@ -372,7 +372,7 @@ export function defineModel< (input) => { const startTimeMs = performance.now(); - return runner(input, getStreamingCallback()).then((response) => { + return runner(input, getStreamingCallback(registry)).then((response) => { const timedResponse = { ...response, latencyMs: performance.now() - startTimeMs, diff --git a/js/ai/src/session.ts b/js/ai/src/session.ts index 6e84c3039..2d5af2fd9 100644 --- a/js/ai/src/session.ts +++ b/js/ai/src/session.ts @@ -16,7 +16,6 @@ import { z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; -import { AsyncLocalStorage } from 'node:async_hooks'; import { v4 as uuidv4 } from 'uuid'; import { Chat, ChatOptions, MAIN_THREAD, PromptRenderOptions } from './chat'; import { @@ -192,7 +191,7 @@ export class Session { maybeOptionsOrPreamble?: ChatOptions | ExecutablePrompt, maybeOptions?: ChatOptions ): Chat { - return runWithSession(this, () => { + return runWithSession(this.registry, this, () => { let options: ChatOptions | undefined; let threadName = MAIN_THREAD; let preamble: ExecutablePrompt | undefined; @@ -266,7 +265,7 @@ export class Session { * `ai.currentSession().state` */ run(fn: () => O) { - return runWithSession(this, fn); + return runWithSession(this.registry, this, fn); } toJSON() { @@ -280,21 +279,24 @@ export interface SessionData { threads?: Record; } -const sessionAls = new AsyncLocalStorage>(); +const sessionAlsKey = 'ai.session'; /** * Executes provided function within the provided session state. */ export function runWithSession( + registry: Registry, session: Session, fn: () => O ): O { - return sessionAls.run(session, fn); + return registry.asyncStore.run(sessionAlsKey, session, fn); } /** Returns the current session. */ -export function getCurrentSession(): Session | undefined { - return sessionAls.getStore(); +export function getCurrentSession( + registry: Registry +): Session | undefined { + return registry.asyncStore.getStore(sessionAlsKey); } /** Throw when session state errors occur, ex. missing state, etc. */ diff --git a/js/ai/src/testing/model-tester.ts b/js/ai/src/testing/model-tester.ts index 7caa4b0cc..626a4c3af 100644 --- a/js/ai/src/testing/model-tester.ts +++ b/js/ai/src/testing/model-tester.ts @@ -170,44 +170,48 @@ export async function testModels( } ); - return await runInNewSpan({ metadata: { name: 'testModels' } }, async () => { - const report: TestReport = []; - for (const test of Object.keys(tests)) { - await runInNewSpan({ metadata: { name: test } }, async () => { - report.push({ - description: test, - models: [], - }); - const caseReport = report[report.length - 1]; - for (const model of models) { - caseReport.models.push({ - name: model, - passed: true, // optimistically + return await runInNewSpan( + registry, + { metadata: { name: 'testModels' } }, + async () => { + const report: TestReport = []; + for (const test of Object.keys(tests)) { + await runInNewSpan(registry, { metadata: { name: test } }, async () => { + report.push({ + description: test, + models: [], }); - const modelReport = caseReport.models[caseReport.models.length - 1]; - try { - await tests[test](registry, model); - } catch (e) { - modelReport.passed = false; - if (e instanceof SkipTestError) { - modelReport.skipped = true; - } else if (e instanceof Error) { - modelReport.error = { - message: e.message, - stack: e.stack, - }; - } else { - modelReport.error = { - message: `${e}`, - }; + const caseReport = report[report.length - 1]; + for (const model of models) { + caseReport.models.push({ + name: model, + passed: true, // optimistically + }); + const modelReport = caseReport.models[caseReport.models.length - 1]; + try { + await tests[test](registry, model); + } catch (e) { + modelReport.passed = false; + if (e instanceof SkipTestError) { + modelReport.skipped = true; + } else if (e instanceof Error) { + modelReport.error = { + message: e.message, + stack: e.stack, + }; + } else { + modelReport.error = { + message: `${e}`, + }; + } } } - } - }); - } + }); + } - return report; - }); + return report; + } + ); } class SkipTestError extends Error {} diff --git a/js/ai/src/tool.ts b/js/ai/src/tool.ts index a56f9ad59..33a9d4645 100644 --- a/js/ai/src/tool.ts +++ b/js/ai/src/tool.ts @@ -72,6 +72,7 @@ export type ToolArgument< * Converts an action to a tool action by setting the appropriate metadata. */ export function asTool( + registry: Registry, action: Action ): ToolAction { if (action.__action?.metadata?.type === 'tool') { @@ -79,7 +80,7 @@ export function asTool( } const fn = ((input) => { - setCustomMetadataAttributes({ subtype: 'tool' }); + setCustomMetadataAttributes(registry, { subtype: 'tool' }); return action(input); }) as ToolAction; fn.__action = { @@ -105,7 +106,7 @@ export async function resolveTools< if (typeof ref === 'string') { return await lookupToolByName(registry, ref); } else if ((ref as Action).__action) { - return asTool(ref as Action); + return asTool(registry, ref as Action); } else if (typeof (ref as ExecutablePrompt).asTool === 'function') { return await (ref as ExecutablePrompt).asTool(); } else if (ref.name) { diff --git a/js/core/src/action.ts b/js/core/src/action.ts index 3428370e3..19c28784e 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -15,7 +15,6 @@ */ import { JSONSchema7 } from 'json-schema'; -import { AsyncLocalStorage } from 'node:async_hooks'; import * as z from 'zod'; import { ActionType, Registry } from './registry.js'; import { parseSchema } from './schema.js'; @@ -105,6 +104,7 @@ export type Action< options?: ActionRunOptions ) => Promise>) & { __action: ActionMetadata; + __registry: Registry; run( input: z.infer, options?: ActionRunOptions> @@ -114,7 +114,7 @@ export type Action< /** * Action factory params. */ -type ActionParams< +export type ActionParams< I extends z.ZodTypeAny, O extends z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny, @@ -169,6 +169,7 @@ export function actionWithMiddleware< return (await wrapped.run(req)).result; }) as Action; wrapped.__action = action.__action; + wrapped.__registry = action.__registry; wrapped.run = async ( req: z.infer, options?: ActionRunOptions> @@ -217,6 +218,7 @@ export function action< O extends z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny, >( + registry: Registry, config: ActionParams, fn: ( input: z.infer, @@ -230,6 +232,7 @@ export function action< const actionFn = async (input: I, options?: ActionRunOptions>) => { return (await actionFn.run(input, options)).result; }; + actionFn.__registry = registry; actionFn.__action = { name: actionName, description: config.description, @@ -250,6 +253,7 @@ export function action< let traceId; let spanId; let output = await newTrace( + registry, { name: actionName, labels: { @@ -259,9 +263,9 @@ export function action< }, }, async (metadata, span) => { - setCustomMetadataAttributes({ subtype: config.actionType }); + setCustomMetadataAttributes(registry, { subtype: config.actionType }); if (options?.context) { - setCustomMetadataAttributes({ + setCustomMetadataAttributes(registry, { context: JSON.stringify(options.context), }); } @@ -345,7 +349,7 @@ export function defineAction< options: ActionFnArg> ) => Promise> ): Action { - if (isInRuntimeContext()) { + if (isInRuntimeContext(registry)) { throw new Error( 'Cannot define new actions at runtime.\n' + 'See: https://github.com/firebase/genkit/blob/main/docs/errors/no_new_actions_at_runtime.md' @@ -356,10 +360,14 @@ export function defineAction< } else { validateActionId(config.name.actionId); } - const act = action(config, async (i: I, options): Promise> => { - await registry.initializeAllPlugins(); - return await runInActionRuntimeContext(() => fn(i, options)); - }); + const act = action( + registry, + config, + async (i: I, options): Promise> => { + await registry.initializeAllPlugins(); + return await runInActionRuntimeContext(registry, () => fn(i, options)); + } + ); act.__action.actionType = config.actionType; registry.registerAction(config.actionType, act); return act; @@ -368,7 +376,7 @@ export function defineAction< // Streaming callback function. export type StreamingCallback = (chunk: T) => void; -const streamingAls = new AsyncLocalStorage>(); +const streamingAlsKey = 'core.action.streamingCallback'; const sentinelNoopCallback = () => null; /** @@ -376,35 +384,43 @@ const sentinelNoopCallback = () => null; * using {@link getStreamingCallback}. */ export function runWithStreamingCallback( + registry: Registry, streamingCallback: StreamingCallback | undefined, fn: () => O ): O { - return streamingAls.run(streamingCallback || sentinelNoopCallback, fn); + return registry.asyncStore.run( + streamingAlsKey, + streamingCallback || sentinelNoopCallback, + fn + ); } /** * Retrieves the {@link StreamingCallback} previously set by {@link runWithStreamingCallback} */ -export function getStreamingCallback(): StreamingCallback | undefined { - const cb = streamingAls.getStore(); +export function getStreamingCallback( + registry: Registry +): StreamingCallback | undefined { + const cb = + registry.asyncStore.getStore>(streamingAlsKey); if (cb === sentinelNoopCallback) { return undefined; } return cb; } -const runtimeCtxAls = new AsyncLocalStorage(); +const runtimeContextAslKey = 'core.action.runtimeContext'; /** * Checks whether the caller is currently in the runtime context of an action. */ -export function isInRuntimeContext() { - return !!runtimeCtxAls.getStore(); +export function isInRuntimeContext(registry: Registry) { + return !!registry.asyncStore.getStore(runtimeContextAslKey); } /** * Execute the provided function in the action runtime context. */ -export function runInActionRuntimeContext(fn: () => R) { - return runtimeCtxAls.run('runtime', fn); +export function runInActionRuntimeContext(registry: Registry, fn: () => R) { + return registry.asyncStore.run(runtimeContextAslKey, 'runtime', fn); } diff --git a/js/core/src/auth.ts b/js/core/src/auth.ts index 753be5153..769865593 100644 --- a/js/core/src/auth.ts +++ b/js/core/src/auth.ts @@ -16,16 +16,24 @@ import { AsyncLocalStorage } from 'node:async_hooks'; import { runInActionRuntimeContext } from './action.js'; +import { HasRegistry, Registry } from './registry.js'; -const contextAsyncLocalStorage = new AsyncLocalStorage(); +const contextAlsKey = 'core.auth.context'; +const legacyContextAsyncLocalStorage = new AsyncLocalStorage(); /** * Execute the provided function in the runtime context. Call {@link getFlowContext()} anywhere * within the async call stack to retrieve the context. */ -export function runWithContext(context: any, fn: () => R) { - return contextAsyncLocalStorage.run(context, () => - runInActionRuntimeContext(fn) +export function runWithContext( + registry: Registry, + context: any, + fn: () => R +) { + return legacyContextAsyncLocalStorage.run(context, () => + registry.asyncStore.run(contextAlsKey, context, () => + runInActionRuntimeContext(registry, fn) + ) ); } @@ -34,13 +42,20 @@ export function runWithContext(context: any, fn: () => R) { * * @deprecated use {@link getFlowContext} */ -export function getFlowAuth(): any { - return contextAsyncLocalStorage.getStore(); +export function getFlowAuth(registry?: Registry | HasRegistry): any { + return getFlowContext(registry); } /** * Gets the runtime context of the current flow. */ -export function getFlowContext(): any { - return contextAsyncLocalStorage.getStore(); +export function getFlowContext(registry?: Registry | HasRegistry): any { + if (!registry) { + return legacyContextAsyncLocalStorage.getStore(); + } + if ((registry as HasRegistry).registry) { + registry = (registry as HasRegistry).registry; + } + registry = registry as Registry; + return registry.asyncStore.getStore(contextAlsKey); } diff --git a/js/core/src/flow.ts b/js/core/src/flow.ts index 9fac492c6..7052180f0 100644 --- a/js/core/src/flow.ts +++ b/js/core/src/flow.ts @@ -18,6 +18,7 @@ import * as bodyParser from 'body-parser'; import cors, { CorsOptions } from 'cors'; import express from 'express'; import { Server } from 'http'; +import { AsyncLocalStorage } from 'node:async_hooks'; import { z } from 'zod'; import { Action, @@ -28,7 +29,7 @@ import { import { runWithContext } from './auth.js'; import { getErrorMessage, getErrorStack } from './error.js'; import { logger } from './logging.js'; -import { Registry } from './registry.js'; +import { HasRegistry, Registry } from './registry.js'; import { runInNewSpan, SPAN_TYPE_ATTR } from './tracing.js'; const streamDelimiter = '\n\n'; @@ -163,7 +164,7 @@ export class Flow< readonly action: Action; constructor( - private registry: Registry, + readonly registry: Registry, config: FlowConfig | StreamingFlowConfig, action: Action ) { @@ -549,16 +550,25 @@ function defineFlowAction< }, async (input, { sendChunk, context }) => { await config.authPolicy?.(context, input); - return await runWithContext(context, () => fn(input, sendChunk)); + return await legacyRegistryAls.run(registry, () => + runWithContext(registry, context, () => fn(input, sendChunk)) + ); } ); } -export function run(name: string, func: () => Promise): Promise; +const legacyRegistryAls = new AsyncLocalStorage(); + +export function run( + name: string, + func: () => Promise, + registry?: Registry +): Promise; export function run( name: string, input: any, - func: (input?: any) => Promise + func: (input?: any) => Promise, + registry?: Registry ): Promise; /** @@ -567,14 +577,47 @@ export function run( export function run( name: string, funcOrInput: () => Promise, - fn?: (input?: any) => Promise + fnOrRegistry?: Registry | HasRegistry | ((input?: any) => Promise), + maybeRegistry?: Registry | HasRegistry ): Promise { - const func = arguments.length === 3 ? fn : funcOrInput; - const input = arguments.length === 3 ? funcOrInput : undefined; + let func; + let input; + let registry: Registry | undefined; + if (typeof funcOrInput === 'function') { + func = funcOrInput; + } else { + input = funcOrInput; + } + if (typeof fnOrRegistry === 'function') { + func = fnOrRegistry; + } else if ( + fnOrRegistry instanceof Registry || + (fnOrRegistry as HasRegistry)?.registry + ) { + registry = (fnOrRegistry as HasRegistry)?.registry + ? (fnOrRegistry as HasRegistry)?.registry + : (fnOrRegistry as Registry); + } + if (maybeRegistry) { + registry = (maybeRegistry as HasRegistry).registry + ? (maybeRegistry as HasRegistry).registry + : (maybeRegistry as Registry); + } + + if (!registry) { + registry = legacyRegistryAls.getStore(); + } + if (!registry) { + throw new Error( + 'Unable to resolve registry. Consider explicitly passing Genkit instance.' + ); + } + if (!func) { throw new Error('unable to resolve run function'); } return runInNewSpan( + registry, { metadata: { name }, labels: { diff --git a/js/core/src/plugin.ts b/js/core/src/plugin.ts index 34276c1d9..ae5bfe340 100644 --- a/js/core/src/plugin.ts +++ b/js/core/src/plugin.ts @@ -15,7 +15,7 @@ */ import { z } from 'zod'; -import { Action, isInRuntimeContext } from './action.js'; +import { Action } from './action.js'; export interface Provider { id: string; @@ -57,12 +57,6 @@ export function genkitPlugin( pluginName: string, initFn: T ): Plugin> { - if (isInRuntimeContext()) { - throw new Error( - 'Cannot define new plugins at runtime.\n' + - 'See: https://github.com/firebase/genkit/blob/main/docs/errors/no_new_actions_at_runtime.md' - ); - } return (...args: Parameters) => ({ name: pluginName, initializer: async () => { diff --git a/js/core/src/reflection.ts b/js/core/src/reflection.ts index 3201e15e5..9ef844503 100644 --- a/js/core/src/reflection.ts +++ b/js/core/src/reflection.ts @@ -167,8 +167,10 @@ export class ReflectionServer { const callback = (chunk) => { response.write(JSON.stringify(chunk) + '\n'); }; - const result = await runWithStreamingCallback(callback, () => - action.run(input, { context, onChunk: callback }) + const result = await runWithStreamingCallback( + this.registry, + callback, + () => action.run(input, { context, onChunk: callback }) ); await flushTracing(); response.write( diff --git a/js/core/src/registry.ts b/js/core/src/registry.ts index ed8414887..2127453f7 100644 --- a/js/core/src/registry.ts +++ b/js/core/src/registry.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { AsyncLocalStorage } from 'node:async_hooks'; import * as z from 'zod'; import { Action } from './action.js'; import { logger } from './logging.js'; @@ -66,6 +67,8 @@ export class Registry { private valueByTypeAndName: Record> = {}; private allPluginsInitialized = false; + readonly asyncStore = new AsyncStore(); + constructor(public parent?: Registry) {} /** @@ -234,3 +237,28 @@ export class Registry { return this.schemasByName[name] || this.parent?.lookupSchema(name); } } + +/** + * Manages AsyncLocalStorage instances in a single place. + */ +export class AsyncStore { + private asls: Record> = {}; + + getStore(key: string): T | undefined { + return this.asls[key]?.getStore(); + } + + run(key: string, store: T, callback: () => R): R { + if (!this.asls[key]) { + this.asls[key] = new AsyncLocalStorage(); + } + return this.asls[key].run(store, callback); + } +} + +/** + * An object that has a reference to Genkit Registry. + */ +export interface HasRegistry { + get registry(): Registry; +} diff --git a/js/core/src/tracing/instrumentation.ts b/js/core/src/tracing/instrumentation.ts index 0dd0173cb..397460c40 100644 --- a/js/core/src/tracing/instrumentation.ts +++ b/js/core/src/tracing/instrumentation.ts @@ -21,13 +21,13 @@ import { SpanStatusCode, trace, } from '@opentelemetry/api'; -import { AsyncLocalStorage } from 'node:async_hooks'; import { performance } from 'node:perf_hooks'; +import { HasRegistry, Registry } from '../registry.js'; import { ensureBasicTelemetryInstrumentation } from '../tracing.js'; import { PathMetadata, SpanMetadata, TraceMetadata } from './types.js'; -export const spanMetadataAls = new AsyncLocalStorage(); -export const traceMetadataAls = new AsyncLocalStorage(); +export const spanMetadataAlsKey = 'core.tracing.instrumentation.span'; +export const traceMetadataAlsKey = 'core.tracing.instrumentation.trace'; export const ATTR_PREFIX = 'genkit'; export const SPAN_TYPE_ATTR = ATTR_PREFIX + ':type'; @@ -38,6 +38,7 @@ const TRACER_VERSION = 'v1'; * */ export async function newTrace( + registry: Registry | HasRegistry, opts: { name: string; labels?: Record; @@ -45,14 +46,21 @@ export async function newTrace( }, fn: (metadata: SpanMetadata, rootSpan: ApiSpan) => Promise ) { + registry = (registry as HasRegistry).registry + ? (registry as HasRegistry).registry + : (registry as Registry); + await ensureBasicTelemetryInstrumentation(); - const traceMetadata: TraceMetadata = traceMetadataAls.getStore() || { + const traceMetadata: TraceMetadata = registry.asyncStore.getStore( + traceMetadataAlsKey + ) || { paths: new Set(), timestamp: performance.now(), featureName: opts.name, }; - return await traceMetadataAls.run(traceMetadata, () => + return await registry.asyncStore.run(traceMetadataAlsKey, traceMetadata, () => runInNewSpan( + registry, { metadata: { name: opts.name, @@ -68,9 +76,10 @@ export async function newTrace( } /** - * + * Runs the provided function in a new span. */ export async function runInNewSpan( + registry: Registry | HasRegistry, opts: { metadata: SpanMetadata; labels?: Record; @@ -79,9 +88,13 @@ export async function runInNewSpan( fn: (metadata: SpanMetadata, otSpan: ApiSpan, isRoot: boolean) => Promise ): Promise { await ensureBasicTelemetryInstrumentation(); + const resolvedRegistry = (registry as HasRegistry).registry + ? (registry as HasRegistry).registry + : (registry as Registry); const tracer = trace.getTracer(TRACER_NAME, TRACER_VERSION); - const parentStep = spanMetadataAls.getStore(); + const parentStep = + resolvedRegistry.asyncStore.getStore(spanMetadataAlsKey); const isInRoot = parentStep?.isRoot === true; if (!parentStep) opts.metadata.isRoot ||= true; return await tracer.startActiveSpan( @@ -96,17 +109,19 @@ export async function runInNewSpan( opts.labels ); - const output = await spanMetadataAls.run(opts.metadata, () => - fn(opts.metadata, otSpan, isInRoot) + const output = await resolvedRegistry.asyncStore.run( + spanMetadataAlsKey, + opts.metadata, + () => fn(opts.metadata, otSpan, isInRoot) ); if (opts.metadata.state !== 'error') { opts.metadata.state = 'success'; } - recordPath(opts.metadata); + recordPath(resolvedRegistry, opts.metadata); return output; } catch (e) { - recordPath(opts.metadata, e); + recordPath(resolvedRegistry, opts.metadata, e); opts.metadata.state = 'error'; otSpan.setStatus({ code: SpanStatusCode.ERROR, @@ -183,8 +198,12 @@ function metadataToAttributes(metadata: SpanMetadata): Record { /** * Sets provided attribute value in the current span. */ -export function setCustomMetadataAttribute(key: string, value: string) { - const currentStep = getCurrentSpan(); +export function setCustomMetadataAttribute( + registry: Registry, + key: string, + value: string +) { + const currentStep = getCurrentSpan(registry); if (!currentStep) { return; } @@ -197,8 +216,11 @@ export function setCustomMetadataAttribute(key: string, value: string) { /** * Sets provided attribute values in the current span. */ -export function setCustomMetadataAttributes(values: Record) { - const currentStep = getCurrentSpan(); +export function setCustomMetadataAttributes( + registry: Registry, + values: Record +) { + const currentStep = getCurrentSpan(registry); if (!currentStep) { return; } @@ -216,8 +238,8 @@ export function toDisplayPath(path: string): string { return Array.from(path.matchAll(pathPartRegex), (m) => m[1]).join(' > '); } -function getCurrentSpan(): SpanMetadata { - const step = spanMetadataAls.getStore(); +function getCurrentSpan(registry: Registry): SpanMetadata { + const step = registry.asyncStore.getStore(spanMetadataAlsKey); if (!step) { throw new Error('running outside step context'); } @@ -236,24 +258,29 @@ function buildPath( return parentPath + `/{${name}${stepType}}`; } -function recordPath(spanMeta: SpanMetadata, err?: any) { +function recordPath(registry: Registry, spanMeta: SpanMetadata, err?: any) { const path = spanMeta.path || ''; const decoratedPath = decoratePathWithSubtype(spanMeta); // Only add the path if a child has not already been added. In the event that // an error is rethrown, we don't want to add each step in the unwind. const paths = Array.from( - traceMetadataAls.getStore()?.paths || new Set() + registry.asyncStore.getStore(traceMetadataAlsKey)?.paths || + new Set() ); const status = err ? 'failure' : 'success'; if (!paths.some((p) => p.path.startsWith(path) && p.status === status)) { const now = performance.now(); - const start = traceMetadataAls.getStore()?.timestamp || now; - traceMetadataAls.getStore()?.paths?.add({ - path: decoratedPath, - error: err?.name, - latency: now - start, - status, - }); + const start = + registry.asyncStore.getStore(traceMetadataAlsKey) + ?.timestamp || now; + registry.asyncStore + .getStore(traceMetadataAlsKey) + ?.paths?.add({ + path: decoratedPath, + error: err?.name, + latency: now - start, + status, + }); } spanMeta.path = decoratedPath; } diff --git a/js/core/tests/action_test.ts b/js/core/tests/action_test.ts index 410cc4f4a..30437639c 100644 --- a/js/core/tests/action_test.ts +++ b/js/core/tests/action_test.ts @@ -15,13 +15,20 @@ */ import assert from 'node:assert'; -import { describe, it } from 'node:test'; +import { beforeEach, describe, it } from 'node:test'; import { z } from 'zod'; import { action } from '../src/action.js'; +import { Registry } from '../src/registry.js'; describe('action', () => { + var registry: Registry; + beforeEach(() => { + registry = new Registry(); + }); + it('applies middleware', async () => { const act = action( + registry, { name: 'foo', inputSchema: z.string(), @@ -31,6 +38,7 @@ describe('action', () => { async (input, opts, next) => (await next(input + 'middle2', opts)) + 2, ], + actionType: 'util', }, async (input) => { return input.length; @@ -45,6 +53,7 @@ describe('action', () => { it('returns telemetry info', async () => { const act = action( + registry, { name: 'foo', inputSchema: z.string(), @@ -54,6 +63,7 @@ describe('action', () => { async (input, opts, next) => (await next(input + 'middle2', opts)) + 2, ], + actionType: 'util', }, async (input) => { return input.length; @@ -79,10 +89,12 @@ describe('action', () => { it('run the action with options', async () => { let passedContext; const act = action( + registry, { name: 'foo', inputSchema: z.string(), outputSchema: z.number(), + actionType: 'util', }, async (input, { sendChunk, context }) => { passedContext = context; diff --git a/js/core/tests/registry_test.ts b/js/core/tests/registry_test.ts index d54fdd415..2b52dde0b 100644 --- a/js/core/tests/registry_test.ts +++ b/js/core/tests/registry_test.ts @@ -28,12 +28,14 @@ describe('registry class', () => { describe('listActions', () => { it('returns all registered actions', async () => { const fooSomethingAction = action( - { name: 'foo_something' }, + registry, + { name: 'foo_something', actionType: 'util' }, async () => null ); registry.registerAction('model', fooSomethingAction); const barSomethingAction = action( - { name: 'bar_something' }, + registry, + { name: 'bar_something', actionType: 'util' }, async () => null ); registry.registerAction('model', barSomethingAction); @@ -53,11 +55,13 @@ describe('registry class', () => { }, }); const fooSomethingAction = action( + registry, { name: { pluginId: 'foo', actionId: 'something', }, + actionType: 'util', }, async () => null ); @@ -69,11 +73,13 @@ describe('registry class', () => { }, }); const barSomethingAction = action( + registry, { name: { pluginId: 'bar', actionId: 'something', }, + actionType: 'util', }, async () => null ); @@ -88,12 +94,14 @@ describe('registry class', () => { const child = Registry.withParent(registry); const fooSomethingAction = action( - { name: 'foo_something' }, + registry, + { name: 'foo_something', actionType: 'util' }, async () => null ); registry.registerAction('model', fooSomethingAction); const barSomethingAction = action( - { name: 'bar_something' }, + registry, + { name: 'bar_something', actionType: 'util' }, async () => null ); child.registerAction('model', barSomethingAction); @@ -140,12 +148,14 @@ describe('registry class', () => { it('returns registered action', async () => { const fooSomethingAction = action( - { name: 'foo_something' }, + registry, + { name: 'foo_something', actionType: 'util' }, async () => null ); registry.registerAction('model', fooSomethingAction); const barSomethingAction = action( - { name: 'bar_something' }, + registry, + { name: 'bar_something', actionType: 'util' }, async () => null ); registry.registerAction('model', barSomethingAction); @@ -169,11 +179,13 @@ describe('registry class', () => { }, }); const somethingAction = action( + registry, { name: { pluginId: 'foo', actionId: 'something', }, + actionType: 'util', }, async () => null ); @@ -194,7 +206,11 @@ describe('registry class', () => { it('should lookup parent registry when child missing action', async () => { const childRegistry = new Registry(registry); - const fooAction = action({ name: 'foo' }, async () => null); + const fooAction = action( + registry, + { name: 'foo', actionType: 'util' }, + async () => null + ); registry.registerAction('model', fooAction); assert.strictEqual(await registry.lookupAction('/model/foo'), fooAction); @@ -209,7 +225,11 @@ describe('registry class', () => { assert.strictEqual(childRegistry.parent, registry); - const fooAction = action({ name: 'foo' }, async () => null); + const fooAction = action( + registry, + { name: 'foo', actionType: 'util' }, + async () => null + ); childRegistry.registerAction('model', fooAction); assert.strictEqual(await registry.lookupAction('/model/foo'), undefined); diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index 673ff8fd3..a7c81abc1 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -121,6 +121,7 @@ import { StreamingFlowConfig, z, } from '@genkit-ai/core'; +import { HasRegistry } from '@genkit-ai/core/registry'; import { defineDotprompt, defineHelper, @@ -168,7 +169,7 @@ export type PromptMetadata< * * There may be multiple Genkit instances in a single codebase. */ -export class Genkit { +export class Genkit implements HasRegistry { /** Developer-configured options. */ readonly options: GenkitOptions; /** Environments that have been configured (at minimum dev). */ @@ -1036,7 +1037,7 @@ export class Genkit { * Gets the current session from async local storage. */ currentSession(): Session { - const currentSession = getCurrentSession(); + const currentSession = getCurrentSession(this.registry); if (!currentSession) { throw new SessionError('not running within a session'); } diff --git a/js/genkit/src/tracing.ts b/js/genkit/src/tracing.ts index 252c8df96..766e0d1ef 100644 --- a/js/genkit/src/tracing.ts +++ b/js/genkit/src/tracing.ts @@ -38,9 +38,7 @@ export { setCustomMetadataAttribute, setCustomMetadataAttributes, setTelemetryServerUrl, - spanMetadataAls, toDisplayPath, - traceMetadataAls, type PathMetadata, type SpanData, type SpanMetadata, diff --git a/js/plugins/checks/src/evaluation.ts b/js/plugins/checks/src/evaluation.ts index a3eb9afcc..bd1fd5df9 100644 --- a/js/plugins/checks/src/evaluation.ts +++ b/js/plugins/checks/src/evaluation.ts @@ -124,6 +124,7 @@ function createPolicyEvaluator( }; const response = await checksEvalInstance( + ai, projectId, auth, partialRequest, @@ -149,12 +150,14 @@ function createPolicyEvaluator( } async function checksEvalInstance( + ai: Genkit, projectId: string, auth: GoogleAuth, partialRequest: any, responseSchema: ResponseType ): Promise> { return await runInNewSpan( + ai, { metadata: { name: 'EvaluationService#evaluateInstances', diff --git a/js/plugins/dotprompt/src/prompt.ts b/js/plugins/dotprompt/src/prompt.ts index a78a14e98..5e55da350 100644 --- a/js/plugins/dotprompt/src/prompt.ts +++ b/js/plugins/dotprompt/src/prompt.ts @@ -182,8 +182,8 @@ export class Dotprompt implements PromptMetadata { */ renderMessages(input?: I, options?: RenderMetadata): MessageData[] { let sessionStateData: Record | undefined = undefined; - if (getCurrentSession()) { - sessionStateData = { state: getCurrentSession()?.state }; + if (getCurrentSession(this.registry)) { + sessionStateData = { state: getCurrentSession(this.registry)?.state }; } input = parseSchema(input, { schema: this.input?.schema, @@ -278,6 +278,7 @@ export class Dotprompt implements PromptMetadata { >(opt: PromptGenerateOptions): Promise> { const spanName = this.variant ? `${this.name}.${this.variant}` : this.name; return runInNewSpan( + this.registry, { metadata: { name: spanName, @@ -288,7 +289,11 @@ export class Dotprompt implements PromptMetadata { }, }, async (metadata) => { - setCustomMetadataAttribute('prompt_fingerprint', this.hash); + setCustomMetadataAttribute( + this.registry, + 'prompt_fingerprint', + this.hash + ); const generateOptions = this._generateOptions(opt); metadata.output = generateOptions; return generateOptions; diff --git a/js/plugins/express/src/index.ts b/js/plugins/express/src/index.ts index 9afdf7dca..0d229e8f0 100644 --- a/js/plugins/express/src/index.ts +++ b/js/plugins/express/src/index.ts @@ -23,6 +23,7 @@ import { z, } from 'genkit'; import { logger } from 'genkit/logging'; +import { Registry } from 'genkit/registry'; import { getErrorMessage, getErrorStack } from './utils'; const streamDelimiter = '\n\n'; @@ -82,6 +83,7 @@ export function handler< ? (f as CallableFlow).flow : undefined; const action: Action = flow ? flow.action : (f as Action); + const registry: Registry = flow ? flow.registry : action.__registry; return async ( request: RequestWithAuth, response: express.Response @@ -121,7 +123,7 @@ export function handler< 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter ); }; - const result = await runWithStreamingCallback(onChunk, () => + const result = await runWithStreamingCallback(registry, onChunk, () => action.run(input, { onChunk, context: auth, diff --git a/js/plugins/vertexai/src/evaluation/evaluator_factory.ts b/js/plugins/vertexai/src/evaluation/evaluator_factory.ts index 821f4631b..4b33144e3 100644 --- a/js/plugins/vertexai/src/evaluation/evaluator_factory.ts +++ b/js/plugins/vertexai/src/evaluation/evaluator_factory.ts @@ -47,6 +47,7 @@ export class EvaluatorFactory { async (datapoint: BaseEvalDataPoint) => { const responseSchema = config.responseSchema; const response = await this.evaluateInstances( + ai, toRequest(datapoint), responseSchema ); @@ -60,11 +61,13 @@ export class EvaluatorFactory { } async evaluateInstances( + ai: Genkit, partialRequest: any, responseSchema: ResponseType ): Promise> { const locationName = `projects/${this.projectId}/locations/${this.location}`; return await runInNewSpan( + ai, { metadata: { name: 'EvaluationService#evaluateInstances', diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index d2c7db228..c1711a784 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -1329,7 +1329,7 @@ importers: version: link:../../plugins/ollama genkitx-openai: specifier: ^0.10.1 - version: 0.10.1(@genkit-ai/ai@0.9.7)(@genkit-ai/core@0.9.7) + version: 0.10.1(@genkit-ai/ai@1.0.0-dev.0)(@genkit-ai/core@1.0.0-dev.0) devDependencies: rimraf: specifier: ^6.0.1 @@ -1384,6 +1384,9 @@ importers: '@genkit-ai/vertexai': specifier: workspace:* version: link:../../plugins/vertexai + '@google-cloud/firestore': + specifier: ^7.11.0 + version: 7.11.0(encoding@0.1.13) firebase-admin: specifier: '>=12.2' version: 12.3.1(encoding@0.1.13) @@ -2143,11 +2146,11 @@ packages: '@firebase/util@1.9.5': resolution: {integrity: sha512-PP4pAFISDxsf70l3pEy34Mf3GkkUcVQ3MdKp6aSVb7tcpfUQxnsdV7twDd8EkfB6zZylH6wpUAoangQDmCUMqw==} - '@genkit-ai/ai@0.9.7': - resolution: {integrity: sha512-CHnN12+J/577EN3uo7qw8tSTa124qwIE+NPHn+TBEtoEkHc70Ck+CxJm0UCfN606hAQyJjj0i6BJ6M4Lr0sMzw==} + '@genkit-ai/ai@1.0.0-dev.0': + resolution: {integrity: sha512-dtQyym12Z/yPw04h7n8n7amU0dpO8iB+uTyDTpSaRe7lkf3SUkNhWM+DDoKUZ70A6cqpAvXN0/haqD9ZpSN+FA==} - '@genkit-ai/core@0.9.7': - resolution: {integrity: sha512-dNKw172HSzgjgRwf8gwyyjYGnopYwfW3iVPHUaCvIXTCX+C/7kJGYea4Xes7b9ushWYOmhG34q/uea7rhLS/Qg==} + '@genkit-ai/core@1.0.0-dev.0': + resolution: {integrity: sha512-mHSAdziskC7YOXkGXCK8299RmGu111dPnxzW2hy8epRpyTfis3SXkB1iJEZE3lxA9tKV9vG2zDxDI0bk4iQbXg==} '@google-cloud/aiplatform@3.25.0': resolution: {integrity: sha512-qKnJgbyCENjed8e1G5zZGFTxxNKhhaKQN414W2KIVHrLxMFmlMuG+3QkXPOWwXBnT5zZ7aMxypt5og0jCirpHg==} @@ -2161,12 +2164,12 @@ packages: resolution: {integrity: sha512-7NBC5vD0au75nkctVs2vEGpdUPFs1BaHTMpeI+RVEgQSMe5/wEU6dx9p0fmZA0bj4HgdpobMKeegOcLUiEoxng==} engines: {node: '>=14.0.0'} - '@google-cloud/firestore@7.6.0': - resolution: {integrity: sha512-WUDbaLY8UnPxgwsyIaxj6uxCtSDAaUyvzWJykNH5rZ9i92/SZCsPNNMN0ajrVpAR81hPIL4amXTaMJ40y5L+Yg==} + '@google-cloud/firestore@7.11.0': + resolution: {integrity: sha512-88uZ+jLsp1aVMj7gh3EKYH1aulTAMFAp8sH/v5a9w8q8iqSG27RiWLoxSAFr/XocZ9hGiWH1kEnBw+zl3xAgNA==} engines: {node: '>=14.0.0'} - '@google-cloud/firestore@7.9.0': - resolution: {integrity: sha512-c4ALHT3G08rV7Zwv8Z2KG63gZh66iKdhCBeDfCpIkLrjX6EAjTD/szMdj14M+FnQuClZLFfW5bAgoOjfNmLtJg==} + '@google-cloud/firestore@7.6.0': + resolution: {integrity: sha512-WUDbaLY8UnPxgwsyIaxj6uxCtSDAaUyvzWJykNH5rZ9i92/SZCsPNNMN0ajrVpAR81hPIL4amXTaMJ40y5L+Yg==} engines: {node: '>=14.0.0'} '@google-cloud/logging-winston@6.0.0': @@ -6927,9 +6930,9 @@ snapshots: dependencies: tslib: 2.6.2 - '@genkit-ai/ai@0.9.7': + '@genkit-ai/ai@1.0.0-dev.0': dependencies: - '@genkit-ai/core': 0.9.7 + '@genkit-ai/core': 1.0.0-dev.0 '@opentelemetry/api': 1.9.0 '@types/node': 20.16.9 colorette: 2.0.20 @@ -6940,7 +6943,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@genkit-ai/core@0.9.7': + '@genkit-ai/core@1.0.0-dev.0': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) @@ -7001,26 +7004,26 @@ snapshots: - encoding - supports-color - '@google-cloud/firestore@7.6.0(encoding@0.1.13)': + '@google-cloud/firestore@7.11.0(encoding@0.1.13)': dependencies: + '@opentelemetry/api': 1.9.0 fast-deep-equal: 3.1.3 functional-red-black-tree: 1.0.1 - google-gax: 4.3.2(encoding@0.1.13) - protobufjs: 7.2.6 + google-gax: 4.4.1(encoding@0.1.13) + protobufjs: 7.3.2 transitivePeerDependencies: - encoding - supports-color - '@google-cloud/firestore@7.9.0(encoding@0.1.13)': + '@google-cloud/firestore@7.6.0(encoding@0.1.13)': dependencies: fast-deep-equal: 3.1.3 functional-red-black-tree: 1.0.1 - google-gax: 4.3.7(encoding@0.1.13) - protobufjs: 7.3.2 + google-gax: 4.3.2(encoding@0.1.13) + protobufjs: 7.2.6 transitivePeerDependencies: - encoding - supports-color - optional: true '@google-cloud/logging-winston@6.0.0(encoding@0.1.13)(winston@3.13.0)': dependencies: @@ -7154,7 +7157,7 @@ snapshots: dependencies: lodash.camelcase: 4.3.0 long: 5.2.3 - protobufjs: 7.2.6 + protobufjs: 7.3.2 yargs: 17.7.2 '@grpc/proto-loader@0.7.13': @@ -9337,7 +9340,7 @@ snapshots: node-forge: 1.3.1 uuid: 10.0.0 optionalDependencies: - '@google-cloud/firestore': 7.9.0(encoding@0.1.13) + '@google-cloud/firestore': 7.11.0(encoding@0.1.13) '@google-cloud/storage': 7.10.1(encoding@0.1.13) transitivePeerDependencies: - encoding @@ -9459,10 +9462,10 @@ snapshots: - encoding - supports-color - genkitx-openai@0.10.1(@genkit-ai/ai@0.9.7)(@genkit-ai/core@0.9.7): + genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-dev.0)(@genkit-ai/core@1.0.0-dev.0): dependencies: - '@genkit-ai/ai': 0.9.7 - '@genkit-ai/core': 0.9.7 + '@genkit-ai/ai': 1.0.0-dev.0 + '@genkit-ai/core': 1.0.0-dev.0 openai: 4.53.0(encoding@0.1.13) zod: 3.23.8 transitivePeerDependencies: @@ -11130,7 +11133,7 @@ snapshots: proto3-json-serializer@2.0.1: dependencies: - protobufjs: 7.2.6 + protobufjs: 7.3.2 proto3-json-serializer@2.0.2: dependencies: diff --git a/js/testapps/rag/package.json b/js/testapps/rag/package.json index 3b2bdf5a5..6a610fc0c 100644 --- a/js/testapps/rag/package.json +++ b/js/testapps/rag/package.json @@ -21,9 +21,10 @@ "@genkit-ai/firebase": "workspace:*", "@genkit-ai/googleai": "workspace:*", "@genkit-ai/vertexai": "workspace:*", + "@google-cloud/firestore": "^7.11.0", + "firebase-admin": ">=12.2", "genkit": "workspace:*", "genkitx-chromadb": "workspace:*", - "firebase-admin": ">=12.2", "genkitx-pinecone": "workspace:*", "google-auth-library": "^9.6.3", "llm-chunk": "^0.0.1", From 94a9ac83d304d865ce40c22b012fff8e5f86bfcb Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Wed, 11 Dec 2024 23:01:06 -0500 Subject: [PATCH 069/562] feat: add Gemini 2.0 Flash Experimental support to Google AI and Vertex AI plugins (#1499) --- js/plugins/googleai/src/gemini.ts | 16 ++++++++++++++++ js/plugins/googleai/src/index.ts | 11 ++++++++++- js/plugins/vertexai/src/gemini.ts | 16 ++++++++++++++++ js/plugins/vertexai/src/index.ts | 2 ++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/js/plugins/googleai/src/gemini.ts b/js/plugins/googleai/src/gemini.ts index 125a62f1e..67b1932d6 100644 --- a/js/plugins/googleai/src/gemini.ts +++ b/js/plugins/googleai/src/gemini.ts @@ -163,6 +163,21 @@ export const gemini15Flash8b = modelRef({ configSchema: GeminiConfigSchema, }); +export const gemini20FlashExp = modelRef({ + name: 'googleai/gemini-2.0-flash-exp', + info: { + label: 'Google AI - Gemini 2.0 Flash (Experimental)', + versions: [], + supports: { + multiturn: true, + media: true, + tools: true, + systemRole: true, + }, + }, + configSchema: GeminiConfigSchema, +}); + export const SUPPORTED_V1_MODELS = { 'gemini-1.0-pro': gemini10Pro, }; @@ -171,6 +186,7 @@ export const SUPPORTED_V15_MODELS = { 'gemini-1.5-pro': gemini15Pro, 'gemini-1.5-flash': gemini15Flash, 'gemini-1.5-flash-8b': gemini15Flash8b, + 'gemini-2.0-flash-exp': gemini20FlashExp, }; export const SUPPORTED_GEMINI_MODELS: Record< diff --git a/js/plugins/googleai/src/index.ts b/js/plugins/googleai/src/index.ts index abb731426..be1de65f1 100644 --- a/js/plugins/googleai/src/index.ts +++ b/js/plugins/googleai/src/index.ts @@ -27,9 +27,18 @@ import { defineGoogleAIModel, gemini10Pro, gemini15Flash, + gemini15Flash8b, gemini15Pro, + gemini20FlashExp, } from './gemini.js'; -export { gemini10Pro, gemini15Flash, gemini15Pro, textEmbeddingGecko001 }; +export { + gemini10Pro, + gemini15Flash, + gemini15Flash8b, + gemini15Pro, + gemini20FlashExp, + textEmbeddingGecko001, +}; export interface PluginOptions { apiKey?: string; diff --git a/js/plugins/vertexai/src/gemini.ts b/js/plugins/vertexai/src/gemini.ts index a9e5f2529..4c45d7db9 100644 --- a/js/plugins/vertexai/src/gemini.ts +++ b/js/plugins/vertexai/src/gemini.ts @@ -131,6 +131,21 @@ export const gemini15Flash = modelRef({ configSchema: GeminiConfigSchema, }); +export const gemini20FlashExp = modelRef({ + name: 'vertexai/gemini-2.0-flash-exp', + info: { + label: 'Vertex AI - Gemini 2.0 Flash (Experimental)', + versions: [], + supports: { + multiturn: true, + media: true, + tools: true, + systemRole: true, + }, + }, + configSchema: GeminiConfigSchema, +}); + export const SUPPORTED_V1_MODELS = { 'gemini-1.0-pro': gemini10Pro, }; @@ -138,6 +153,7 @@ export const SUPPORTED_V1_MODELS = { export const SUPPORTED_V15_MODELS = { 'gemini-1.5-pro': gemini15Pro, 'gemini-1.5-flash': gemini15Flash, + 'gemini-2.0-flash-exp': gemini20FlashExp, }; export const SUPPORTED_GEMINI_MODELS = { diff --git a/js/plugins/vertexai/src/index.ts b/js/plugins/vertexai/src/index.ts index 688be3f82..6648ca686 100644 --- a/js/plugins/vertexai/src/index.ts +++ b/js/plugins/vertexai/src/index.ts @@ -32,6 +32,7 @@ import { gemini10Pro, gemini15Flash, gemini15Pro, + gemini20FlashExp, } from './gemini.js'; import { SUPPORTED_IMAGEN_MODELS, @@ -45,6 +46,7 @@ export { gemini10Pro, gemini15Flash, gemini15Pro, + gemini20FlashExp, imagen2, imagen3, imagen3Fast, From 9f174567f64ab9b77cb6ae49bb5e6aa47350469e Mon Sep 17 00:00:00 2001 From: Michael Doyle Date: Thu, 12 Dec 2024 09:47:49 -0500 Subject: [PATCH 070/562] docs: Updated Google Cloud plugin instructions (#1494) --- docs/plugins/google-cloud.md | 337 ++++++++++++++++-- .../cloud-ops-logs-explorer-menu.png | Bin 49181 -> 450225 bytes docs/resources/cloud-ops-metrics-explorer.png | Bin 69562 -> 261231 bytes docs/resources/cloud-ops-metrics-mgmt.png | Bin 40403 -> 560662 bytes 4 files changed, 299 insertions(+), 38 deletions(-) diff --git a/docs/plugins/google-cloud.md b/docs/plugins/google-cloud.md index 85e68233d..093047e2b 100644 --- a/docs/plugins/google-cloud.md +++ b/docs/plugins/google-cloud.md @@ -1,9 +1,10 @@ # Google Cloud plugin -The Google Cloud plugin exports Firebase Genkit's telemetry and logging data to -[Google Cloud's operations suite](https://cloud.google.com/products/operations) which powers the [Firebase AI Monitoring dashboard (private preview)](https://forms.gle/Lp5S1NxbZUXsWc457). - -> Note: Logging is facilitated by [Winston](https://github.com/winstonjs/winston) in favor of the [OpenTelemetry](https://opentelemetry.io/) logging APIs. Export of logs is done via a dedicated Winston Google Cloud exporter. +The Google Cloud plugin exports Firebase Genkit telemetry and logging data to +the +[Cloud Observability](https://cloud.google.com/products/operations) +suite, which powers the +[Firebase AI Monitoring](https://forms.gle/Lp5S1NxbZUXsWc457) dashboard. ## Installation @@ -11,26 +12,34 @@ The Google Cloud plugin exports Firebase Genkit's telemetry and logging data to npm i --save @genkit-ai/google-cloud ``` -If you want to locally run flows that use this plugin, you also need the -[Google Cloud CLI tool](https://cloud.google.com/sdk/docs/install) installed. +When running Genkit code locally that includes this plugin, you will also need +the [Google Cloud CLI tool](https://cloud.google.com/sdk/docs/install) +installed. ## Set up a Google Cloud account -This plugin requires a Google Cloud account ([sign up](https://cloud.google.com/gcp) if you don't already have one) and a Google Cloud project. +This plugin requires a Google Cloud account/project. All Firebase projects +include one by default ([GCP Console](https://console.cloud.google.com)), +or you can sign up at https://cloud.google.com. -Prior to adding the plugin, make sure that the following APIs are enabled for your project: +Prior to adding the plugin, make sure that the following APIs are enabled for +your GCP project: - [Cloud Logging API](https://console.cloud.google.com/apis/library/logging.googleapis.com) - [Cloud Trace API](https://console.cloud.google.com/apis/library/cloudtrace.googleapis.com) - [Cloud Monitoring API](https://console.cloud.google.com/apis/library/monitoring.googleapis.com) -These APIs should be listed in the [API dashboard](https://console.cloud.google.com/apis/dashboard) for your project. +These APIs should be listed in the +[API dashboard](https://console.cloud.google.com/apis/dashboard) for your +project. -Click [here](https://support.google.com/googleapi/answer/6158841) to learn more about enabling and disabling APIs. +Click [here](https://support.google.com/googleapi/answer/6158841) to learn more +about enabling and disabling APIs. ## Genkit configuration -To enable exporting to Google Cloud Tracing, Logging, and Monitoring, simply call `enableGoogleCloudTelemetry()`: +To enable Cloud Tracing, Logging, and Monitoring (metrics), simply call +`enableGoogleCloudTelemetry()`: ```ts import { enableGoogleCloudTelemetry } from '@genkit-ai/google-cloud'; @@ -38,35 +47,70 @@ import { enableGoogleCloudTelemetry } from '@genkit-ai/google-cloud'; enableGoogleCloudTelemetry(); ``` -When running in production, your telemetry gets automatically exported. +When running in production, telemetry will be exported automatically. -### Authentication +### Authentication and authorization -The plugin requires the Google Cloud project ID and your Google Cloud project credentials. If you're running your flow from a Google Cloud environment (Cloud Functions, Cloud Run, etc), the project ID and credentials are set automatically. +The plugin requires a Google Cloud project ID and application credentials. -#### Application Default Credentials +#### Google Cloud -Running in other environments requires setting the `GCLOUD_PROJECT` environment variable to your Google Cloud project and authenticating using the `gcloud` tool: +If deploying your code to a Google Cloud environment (Cloud +Functions, Cloud Run, etc), the project ID and credentials will be discovered +automatically via +[Application Default Credentials](https://cloud.google.com/docs/authentication/provide-credentials-adc). -```posix-terminal -gcloud auth application-default login -``` +You will need to apply the following roles to the service account that is +running your code (i.e. 'attached service account') via the +[IAM Console](https://pantheon.corp.google.com/iam-admin/iam): -For more information, see the [Application Default Credentials](https://cloud.google.com/docs/authentication/provide-credentials-adc) docs. +- `roles/monitoring.metricWriter` +- `roles/cloudtrace.agent` +- `roles/logging.logWriter` -#### Service Account Credentials +#### Local Development -If you are using a service account and running outside of a Google Cloud environment, you can set your credentials as an environment variable. Follow instructions here to [set up your Google Cloud Service Account Key](https://cloud.google.com/iam/docs/keys-create-delete#creating). +When doing local development, in order for your user credentials to be available +to the plugin, additional steps are required. -Once you have downloaded the key file, you can specify the credentials in two ways; a file location using the `GOOGLE_APPLICATION_CREDENTIALS` environment variable or directly copy the contents of the json file to the environment variable `GCLOUD_SERVICE_ACCOUNT_CREDS`. +1. Set the `GCLOUD_PROJECT` environment variable to your Google Cloud project. -File path: +2. Authenticate using the `gcloud` CLI: -``` -GOOGLE_APPLICATION_CREDENTIALS = "path/to/your/key/file" -``` + ```posix-terminal + gcloud auth application-default login + ``` + +#### Production environments outside of Google Cloud + +If possible, it is still recommended to leverage the +[Application Default Credentials](https://cloud.google.com/docs/authentication/provide-credentials-adc) +process to make credentials available to the plugin. + +Typically this involves generating a service account key/pair and deploying +those credentials to your production environment. + +1. Follow the instructions to set up a +[service account key](https://cloud.google.com/iam/docs/keys-create-delete#creating). -Direct copy: +2. Ensure the service account has the following roles: + - `roles/monitoring.metricWriter` + - `roles/cloudtrace.agent` + - `roles/logging.logWriter` + +3. Deploy the credential file to production (**do not** check into source code) + +4. Set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable as the path to +the credential file. + + ``` + GOOGLE_APPLICATION_CREDENTIALS = "path/to/your/key/file" + ``` + +In some serverless environments, you may not be able to deploy a credential +file. In this case, as an alternative to steps 3 & 4 above, you can set the +`GCLOUD_SERVICE_ACCOUNT_CREDS` environment variable with the contents of the +credential file as follows: ``` GCLOUD_SERVICE_ACCOUNT_CREDS='{ @@ -85,7 +129,10 @@ GCLOUD_SERVICE_ACCOUNT_CREDS='{ ## Plugin configuration -The `enableGoogleCloudTelemetry()` function takes an optional configuration object which configures the [OpenTelemetry NodeSDK](https://open-telemetry.github.io/opentelemetry-js/classes/_opentelemetry_sdk_node.NodeSDK.html) instance. +The `enableGoogleCloudTelemetry()` function takes an optional configuration +object which configures the +[OpenTelemetry NodeSDK](https://open-telemetry.github.io/opentelemetry-js/classes/_opentelemetry_sdk_node.NodeSDK.html) +instance. ```ts import { AlwaysOnSampler } from '@opentelemetry/sdk-trace-base'; @@ -102,14 +149,18 @@ enableGoogleCloudTelemetry({ metricExportIntervalMillis: 5_000, }); ``` -The configuration objects allows fine grained control over various aspects of the telemetry export outlined below. +The configuration objects allows fine grained control over various aspects of +the telemetry export outlined below. #### credentials -Allows specifying credentials directly using [JWTInput](http://cloud/nodejs/docs/reference/google-auth-library/latest/google-auth-library/jwtinput) from the google-auth library. +Allows specifying credentials directly using +[JWTInput](http://cloud/nodejs/docs/reference/google-auth-library/latest/google-auth-library/jwtinput) +from the google-auth library. #### sampler -For cases where exporting all traces isn't practical, OpenTelemetry allows trace [sampling](https://opentelemetry.io/docs/languages/java/instrumentation/#sampler). +For cases where exporting all traces isn't practical, OpenTelemetry allows trace +[sampling](https://opentelemetry.io/docs/languages/java/instrumentation/#sampler). There are four preconfigured samplers: @@ -120,7 +171,11 @@ There are four preconfigured samplers: #### autoInstrumentation & autoInstrumentationConfig -Enabling [automatic instrumentation](https://opentelemetry.io/docs/languages/js/automatic/) allows OpenTelemetry to capture telemetry data from [third-party libraries](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/metapackages/auto-instrumentations-node/src/utils.ts) without the need to modify code. +Enabling +[automatic instrumentation](https://opentelemetry.io/docs/languages/js/automatic/) +allows OpenTelemetry to capture telemetry data from +[third-party libraries](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/metapackages/auto-instrumentations-node/src/utils.ts) +without the need to modify code. #### metricExportIntervalMillis @@ -134,11 +189,13 @@ This field specifies the timeout for the metrics export in milliseconds. #### disableMetrics -Provides an override that disables metrics export while still exporting traces and logs. +Provides an override that disables metrics export while still exporting traces +and logs. #### disableTraces -Provides an override that disables exporting traces while still exprting metrics and logs. +Provides an override that disables exporting traces while still exprting metrics +and logs. #### disableLoggingIO @@ -146,10 +203,214 @@ Provides an override that disables collecting input and output logs. #### forceDevExport -This option will force Genkit to export telemetry and log data when running in the `dev` environment (e.g. locally). +This option will force Genkit to export telemetry and log data when running in +the `dev` environment (e.g. locally). -> Note: When running locally, internal telemetry buffers may not fully flush prior to the process exiting, resulting in an incomplete telemetry export. +> Note: When running locally, internal telemetry buffers may not fully flush +prior to the process exiting, resulting in an incomplete telemetry export. ## Test your integration -When configuring the plugin, use `forceDevExport: true` to enable telemetry export for local runs. Navigate to the Google Cloud Logs, Metrics, or Trace Explorer to view telemetry. Alternatively, navigate to the [Firebase AI Monitoring dashboard (private preview)](https://forms.gle/Lp5S1NxbZUXsWc457) for an AI-idiomatic view of telemetry. +When configuring the plugin, use `forceDevExport: true` to enable telemetry +export for local runs. Navigate to the Google Cloud Logs, Metrics, or Trace +Explorer to view telemetry. + +# Google Cloud Observability suite + +Once your code (e.g. flow) is deployed, navigate to the +[Cloud Monitoring](https://console.cloud.google.com/monitoring/) dashboard and +select your project. From here, you can easily navigate between the Logs, +Metrics and Trace explorers for production monitoring. + + + +## Logs and traces + +From the left hand side menu, click 'Logs explorer' under the 'Explore' heading. + + + +Here, you will see all logs that are associated with your deployed Genkit code, +including `console.log()`. Any log which has the prefix `[genkit]` is a +Genkit-internal log that contains information that may be interesting for +debugging purposes. For example, Genkit logs in the format `Config[...]` contain +metadata such as the temperature and topK values for specific LLM inferences. +Logs in the format `Output[...]` contain LLM responses while `Input[...]` logs +contain the prompts. Cloud Logging has robust ACLs that allow fine grained +control over access to sensitive logs. + +> Note: Prompts and LLM responses are redacted from trace attributes in Cloud +Trace. For specific log lines, it is possible to navigate to their respective +traces by clicking on the extended menu + icon and +selecting "View in trace details". + + + +This will bring up a trace preview pane providing a quick glance of the details +of the trace. To get to the full details, click the "View in Trace" link at the +top right of the pane. + + + +The most prominent navigation element in Cloud Trace is the trace scatter plot. +It contains all collected traces in a given time span. + + + +Clicking on each data point will show its details below the scatter plot. + + + +The detailed view contains the flow shape, including all steps, and important +timing information. Cloud Trace has the ability to interleave all logs +associated with a given trace within this view. Select the "Show expanded" +option in the "Logs & events" drop down. + + + +The resultant view allows detailed examination of logs in the context of the +trace, including prompts and LLM responses. + + + +## Metrics + +Viewing all metrics exported by Genkit can be done by clicking on +'Metrics management' under the 'Configure' heading in the left hand side menu. + + + +The metrics management console contains a tabular view of all collected metrics, +including those that pertain to Cloud Run and its surrounding environment. +Clicking on the 'Workload' option will reveal a list that includes +Genkit-collected metrics. Any metric with the `genkit` prefix constitutes an +internal Genkit metric. + + + +Genkit collects several categories of metrics including: feature, action, and +generate. Each metric has several useful dimensions facilitating robust +filtering and grouping. + +Common dimensions include: + +- `flow_name` - the top-level name of the flow. +- `flow_path` - the span and its parent span chain up to the root span. +- `error_code` - in case of an error, the corresponding error code. +- `error_message` - in case of an error, the corresponding error message. +- `model` - the name of the model. + +### Feature metrics + +Features are the top-level entry-point to your Genkit code. In most cases, this +will be a flow. Otherwise, this will be the top-most span in a trace. + +| Name | Type | Description | +| ----------------------- | --------- | ----------------------- | +| genkit/feature/requests | Counter | Number of requests | +| genkit/feature/latency | Histogram | Execution latency in ms | + +Each feature metric contains the following dimensions: + +| Name | Description | +| ------------- | -------------------------------------------------------------------------------- | +| name | The name of the feature. In most cases, this is the top-level Genkit flow | +| status | 'success' or 'failure' depending on whether or not the feature request succeeded | +| error | Only set when `status=failure`. Contains the error type that caused the failure | +| source | The Genkit source language. Eg. 'ts' | +| sourceVersion | The Genkit framework version | + + +### Action metrics + +Actions represent a generic step of execution within Genkit. Each of these steps +will have the following metrics tracked: + +| Name | Type | Description | +| ----------------------- | --------- | --------------------------------------------- | +| genkit/action/requests | Counter | Number of times this action has been executed | +| genkit/action/latency | Histogram | Execution latency in ms | + +Each action metric contains the following dimensions: + +| Name | Description | +| ------------- | ---------------------------------------------------------------------------------------------------- | +| name | The name of the action | +| featureName | The name of the parent feature being executed | +| path | The path of execution from the feature root to this action. eg. '/myFeature/parentAction/thisAction' | +| status | 'success' or 'failure' depending on whether or not the action succeeded | +| error | Only set when `status=failure`. Contains the error type that caused the failure | +| source | The Genkit source language. Eg. 'ts' | +| sourceVersion | The Genkit framework version | + +### Generate metrics + +These are special action metrics relating to actions that interact with a model. +In addition to requests and latency, input and output are also tracked, with +model specific dimensions that make debugging and configuration tuning easier. + +| Name | Type | Description | +| ------------------------------------ | --------- | ------------------------------------------ | +| genkit/ai/generate/requests | Counter | Number of times this model has been called | +| genkit/ai/generate/latency | Histogram | Execution latency in ms | +| genkit/ai/generate/input/tokens | Counter | Input tokens | +| genkit/ai/generate/output/tokens | Counter | Output tokens | +| genkit/ai/generate/input/characters | Counter | Input characters | +| genkit/ai/generate/output/characters | Counter | Output characters | +| genkit/ai/generate/input/images | Counter | Input images | +| genkit/ai/generate/output/images | Counter | Output images | +| genkit/ai/generate/input/audio | Counter | Input audio files | +| genkit/ai/generate/output/audio | Counter | Output audio files | + +Each generate metric contains the following dimensions: + +| Name | Description | +| --------------- | ---------------------------------------------------------------------------------------------------- | +| modelName | The name of the model | +| featureName | The name of the parent feature being executed | +| path | The path of execution from the feature root to this action. eg. '/myFeature/parentAction/thisAction' | +| latencyMs | The response time taken by the model | +| status | 'success' or 'failure' depending on whether or not the feature request succeeded | +| error | Only set when `status=failure`. Contains the error type that caused the failure | +| source | The Genkit source language. Eg. 'ts' | +| sourceVersion | The Genkit framework version | + +Visualizing metrics can be done through the Metrics Explorer. Using the left +hand side menu, click 'Metrics explorer' under the 'Explore' heading. + + + +Select a metrics by clicking on the 'Select a metric' dropdown, selecting +'Generic Node', 'Genkit', and a metric. + + + +The visualization of the metric will depend on its type (counter, histogram, +etc). The Metrics Explorer provides robust aggregation and querying facilities +to help graph metrics by their various dimensions. + + + +## Telemetry delay + +There may be a slight delay before telemetry for a particular execution of a +flow is displayed in Cloud's operations suite. In most cases, this delay is +under 1 minute. + +## Quotas and limits + +There are several quotas that are important to keep in mind: + +- [Cloud Trace Quotas](http://cloud.google.com/trace/docs/quotas) +- [Cloud Logging Quotas](http://cloud.google.com/logging/quotas) +- [Cloud Monitoring Quotas](http://cloud.google.com/monitoring/quotas) + +## Cost + +Cloud Logging, Cloud Trace, and Cloud Monitoring have generous free tiers. +Specific pricing can be found at the following links: + +- [Cloud Logging Pricing](http://cloud.google.com/stackdriver/pricing#google-cloud-observability-pricing) +- [Cloud Trace Pricing](https://cloud.google.com/trace#pricing) +- [Cloud Monitoring Pricing](https://cloud.google.com/stackdriver/pricing#monitoring-pricing-summary) diff --git a/docs/resources/cloud-ops-logs-explorer-menu.png b/docs/resources/cloud-ops-logs-explorer-menu.png index dddb0868f3f31c718a31723cafb6606761d2513e..9968ab384d6909becf6441312761889e0e002d3b 100644 GIT binary patch literal 450225 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

A*FJr7fn39PVBFR5qF*jIax zfb_%l1n30qH&`R~8vNg4Xc+=N-cs_(a3$tc-IJf8H_!57u6(duduXqfZd_07U$eJ8 z-kC11ZPEnY#;FV)pKD-lEZ~DgmFjr$5wp^oE)Q2E!iDWal1>aWubuf=Nq~^Mzfa;L zYT2#0MOegRr(^$>O?;Wn!=?7Ig)k6{@)%x8)Y>f9P-c82VQwgNcM}1b$_Z7&b7#^7 zIg3-avZS%bURz-!tAW!}rRc$mAcg&?|T~9HWjowghu>DzkD^^KY z_4gba(t;FQLBX`Gcp`lZY(nTH`lWoxah9Qo%k9XD$7EdV-rm$8FmHHm^=|0Xbj5b+ z$lc{OGkv4(w{~nM4d;4v&E~H^QS+QNPO}fV zb(??b0`V9_GepE7$&J{rP-0a=$;F@fmqDe~mGfRR$z)Q>nKPZy_vNr7#Tjt)=Zor9 z*bAZ0J4Q39Qrv@_nCzkXNPA9)!X~*I+;$s5Z-5tLeZjOf>`Y4iqT>u~WqWyCQ zfQVgA0+#p~CTF2+n`ko-z-}cbaK3k?fyQ57*(tZZ{)|Ka!~Dlg^bN7lgGjdTeSPuj zs%O%z=$nB5vXi`PPMC!4b$gMC;kQN~f&5q61L;PZEh(qnwPd44JCHKlX}4pom|FXz z<$NE~lGO_!R^)fWkMBpC^7qW17rCXhb;q( z!x2}r-5e1X(xOnbD;A_pI~RfT&>~c zAlcg@ori@?n6o-umB~3;cfApK-#R3aICNVm%+COgej^+Uq`A`)sJ$Pt9=M>qgK8P~ z#xnD9-ZyJHd^;|Txp->V%!I^K+hme_P{E}7m-E$!iVDjWv{=oQuGkZX*&+e@-K9&4@}4`q+wC{_^?MJT<=Csw3Ec;}q8SR=c)AWF z-z78C=R(Gv%z)~>#CTtzK6FD6g#IJjR<2jk#DOB4B=MConiW!}_ZfNoyD51;>{eqZOTm4q!)-#qih@%c5MR8l69bnJ8Mk68RQ znR=m{=M4msS$pQ-{u?j-UsASfGVASe)aGz%;hOAcAWtzJ5Yy^_V&SUxEv;`{(6g3UjQEKz$Sdmm164MDArr{YgA1?H9U8 zVoEs%$n5=ziVfRf*68VN1Q7YtbdbVFEn|^$sW^9!yu;#iSVW8!pXKNhM~LZja}Zw?-aeVb&~D%XDYS3w#upy}FF@1h*Y- zAdhFlUXW~tgeGv=JgXlFL{ES#m`y~(G`W#ej3Z!kFKD!qS>^jq@V+xBi2Cn))B6mL zbdO^#rITN3lI2GhpS&g4-ya6CB_zCp;&p&x1pMclOsa(8KNDu~g$SpQcNw+cFlV)5 z00FP(7cm5?Tn6X;H!^2!xx5L~P!pzv(|IO-Ju=G1Pd>WwnZgX&(W+PClu1q$*c za4=uKe0#NdqW7}_`$KSD`8`@ium};8_})yNe~$xns;3&}Dx1gw!*g5!3Gb8*jmZ{z zfyg0Njpm1{;UsEOSiD761n7h=)H}RN93rezx1Jww@zXv3CUihIGWM9DYpH zAaicl<5=1~^9woFb8usC82-XyGfV;6hS~jBm)mbc4HK~mM+-9}WE1&l>7&lOyKouR z816xis;k}6HgrqI)6`p@DrdIcFSxzt#V1!99yN^I^PYn+NotI368_q-fB#JXo3BaX zuV zI2CwG;3$>Ak*@&!&nE!>QO|U!BTTCYWZ6K1P%vRFSs zQMJ-BX%-ZS%L}8h(CCR8w^K#z>&Y8w&nj^mvR9tHs0Li^7fQv*pejMOU-v6CvK(2} z+H-T1y@hlq4wPIpY8|DmM4gY9!vs}DvhRWR!x?WJlD+Qg?`~_STBQGA0bC82OB!qL z+#8Djuy7G(5Yy)b^5FeUsi>NhEqXi?SYT6KP9@1KO~Yn!yfZZS@!>oesSPIPkH^I( zdqLnYCW9a=2q?gj4s>WqSaML_LG5bw1b(-I`Foh|SE{~DnOfc-Q3?e!0(aNXlO`&p zdEf4e0Rowm`S`I#(5ly!qh=4Wz%(i{zw;z+mGuE{r9h&F85UOL&1F{pL@D zsTj+a!lFe&8pmz2 z^R_S&+4mZaX{}Xh7XAlN!=1^Y(7oOrhpCBsHKX(s&aXDh)FxI!VvTh7m6)}YkbY{DtHmpk^sGB~|^tKB0t@L+b9nh_F$MakyiQ+AeH&};r6eS}p8EqO! zUfs$)p-odbw}w(AqwqT(4pT}x`)zNJkBJW&?1I8uiy9gUheG@d%O3f|JoV_@nW1D%LRg~$F}X7Bp8b~JZEE{Nk3C@g4HIS(C$ z!f}Aw$FT<1-`eb?kyu4RWWOGZj3d8s9&`KyD)M{5@jZ<0ir9x@?=nhZM)z~1G(zIS zZ1~56>UPj*|uzk5MC)pEhp0wRK5*wI0!r3G#zMz2fCP@8_!E_I)e)pf9}LDkzkj z4`J~!2s6Nuy*mZ2U7)c4_Rb}XSph*&A52Lc!=j$$CmdBQzXu@vM;~l;kfe?DUQG8u zA6CDgGi$!_5=Rc~&mPn|81_59zBuYG`TmJg*ca|pvra8&5J|5P2r#$8&2{WSql*z& zB&!zath>1=^L(i@Q}JU9wHse~RgxZG0^G_8d)*_BybReGCABgWD$u%k?ABSE`ZVn7 zuLio%Yf^}qOWTXn^%Spo#g5f)>%_0W5CF5M4mPPjIK?8pr8Wf!hHrz=d7DUC4s?cx}IRss3y}N~_*= z0|o08Fq%eS9!i0!fwIo_voCeBUBFz0L9^s_$_PkEPC}6^ag~C#XVFo*PqxRasIR7z z0hY4x#hHKS7jko1e$c0k{M7w?0Q^b$9Yr2b2KfV@>gKREs(>d!{~<393`WYr*o{B_ z=H1R5qWfdTi6&@-GetBh&lam_}_;Duo8t=4*@X&#M)o!oP`nzKDf7ue5WqIE=aF8}>V-R&$c&;ly@#9ZH!^Es4o@dAxocZf{ z?&_&@o}AgiBKEg&U%x*4&d*|MPF;0FT;{mT;&vi`r*%zq_w9DL%6p3`wV7SB){$|< z)XZASE+Xl*w&Ku>R_X2W{B8%D8^L&xmc*DZe03bQGXpcpZ#3x)6Q<3(D!NVLfuKpj zD#*=XF;Nezcts^=R|G~VLuTx1xfX8z4MRZg)n!2)cz>(eWe5~qEEp_intR*e}I{GPSuc6o98mlE{x z@omZlA>04v7fe$sq@h7RApp{TM@Ba_l~Tuia|gdbbd4T{zid`VH6FSNWH+zh#pf;k zEC-0*H%3N3%Byu@*S`MbE++nSE#APKYP>D1X7l3QOr}`|7ai>vl~TRwUuulfYo4K$ zd(Mw7>{r&-h~YE4X#dR&O(DQLx~1IQmRa@aJ1wPq)3PqR;r>@jZIziP>=rb0wGO`~ z!=nCjA7Yo(S=@xx#H+K01{JKfppv*3HI-NdHoLgz~$*c|sK{UZcNoIiZ zT5nXIA7Z|=ak4|8iITLoDTbh+pmsHG0qh^|)AQ0F6et?? z^D!|oV}L6a-exPxpFiOfPkp~|quwW49j9g9UK%$My~#Hv;yWM*=rS*?e{YG3f)<$@ zh69310I8w*9?m&>&U=1uobMm6nQP>E z_FjAKwb#1$TK9?)%b=u`K8>+C`%_M<4{P^(8COmT^dk!knG#H|#2bM`h`Pmh2RVQi zm%sUnKAiVORI_x5Nc;MsR}YOuALQ||_^(#?d*Vn~w}@POI)H~jB>N)u9H;O&9Jj4T z3N%=UZ^wI`otnDXpBatwG+ks67*JPP_?@Mr#=ZZgyDDVAEx<}ol=0rF1T`I+M{Gxfnb)^)M~KZaha{O9WPlY~g)G3tV9 z18lnAcZ68S-M5pqQ$(c>1txfcY+1kU0g_lThl^_%xtA_8@4RybH4Pnsn-$O}HHskB zOC8`E)_+UR^QShyDi(7{mbX|uZFi&XaohD!`QBm4lbNr&8|Qs1&8@nweanREXM;qQ z6x7el?&Y67c~9Xr3qA2=HGRc;7&M+VHM8zn!Gw?`yj#2Z%adhJ-R~Oh8o^Ec20*4D zJYT170`M*rxNsw@L4D29CW=Y1ZcGSL(ss-`NNMmd@c>Q4?ogy7H@T8B0@uqvhoVQ1 z0)vB@ym3fLK}FX#OXsMVc;QPZKL_LOeHRrCErIFTsB!O#TVY=5<>jRwTm5Xc6|v~Pe+vVa%SC46$Cld@?MA^{LaQmyyZX#)gK@09`l?z0%v!A^K z9TRVI*UKi~1UPic4*U&EoERl1LVt=7LT^Z$LHB-)+F3>Izwyk)Yxdg~Gl2CE?G z^85dq4aYsa!$sVGJT4LD#qPnu;|rtJT& z-(M8*#<(;yiM58adNt@0|Kq9np#mC!+9v&9NAlMZyK8xSQyN@3*75w~Ke>y+cX4rX zx9IyXoAy6va_8jpEkb|yqomdPf2;s|6c5537rl*5P2DEj8UFF;9_(@drUNBO81fBD$pvL}RWizDt`h)zlcSf052rV6Z>%E*gb@|Lelw)wBj;D#Bexq=jyDp6bjRy>%pzP7>AWmE=Z$8Vv=aL$zCN_b#l3&AUm7H z>`Cjmwo+WxzOW4|I7q>o!R}qDla1|Bv zyLS?qoZ!Xv=I15b*Gi}tT%ufDUeX8(HfX0jqWhbMs#GBdltVygGYv()u8;~b@LJ>H zhDgD+qHk8AcLu=#OwDp+^%DHjc|lxxV!K&^g*F`#w_TMqPzqK z{osdxD02E|v@3*BBGWnC+hG!Y7-=NK{D(L@KmQY41KCI>&Jqj^M@=NDey7i8#LLb< zH;dR2?#cSH@+v}fT$8Ta4ME1g)jt{}+;a+Uq!DzYkvT9?1odV&1i`!fzoR)*mpKzx z|B}vc&)}A#pdB%oUJ>4Cw1(tv^Af(7v$)oQHxg!hoH;>6#OFU1S8O^s$G5yk#lJpO zmR&9%s1Spz1e}};DkgQl)SIoqvHW8Ki!yhUA-eHfd-9&WX^i#i^~{Al3C!G@GBXKX z&-gya!@NvI_SSkdhD++msee%3S8HcS2$U-}T^t2r!?%I-yy zEglD-@2?5tQ4HlsmYOZrR}f%y(;|G)!w4AwF27o zDyyWWoHf-uqZi-A{a=j1;?swGb(YK;_{4~N&-|3HgADf59*ZKJVOV4h%Dw_fIN@;^ zUHY)AV%|_kcu%aAY=B&2;7YSHh}DuO$IFQsy ztA~ZUQL}M^8R)An;eBhYr1Si$@>0K z1t3&3g`x478n1QlUYoc!s-%b(X(w4*1qER|LTm@l_dAVnLW=5iEL?jC*@ej(Bh@Wj z3{uE9+@UkM2cN700}Ad?OPwX@$bS?Z<=kFbK}j^c`xkLz18|x(`QlXJ)h3sZ1kb4Z2=|Hx3gL6ycxx>fP zE|)7&6Tu|9;oauS&@s+)v-7KxdkI1l2^J@(Gn?{3=sx4Fml;O7&I^vReccf{0nZ6~ zDj^fw)Ta&-{*@mwBTd194se*cc-Pr_!>fezQ%_5w24$I2iDIJ1l)mTV_+HqOjd53w zVyV|=Y1@he*J!@zvUB{Eq4w5f#@I$p*olw9dRyG4QoT-F9M9#O%!xg$qx-KJBwvn{ zueP&faBS??kGmqsISIBSlv|W>x%KV6lFh;gPgTDcu^6#_jO7evjt9x0l+NqNJ`n{J z3?g_MuWdK#@&boY{m`)2j@fn5fXBGE(9g}dfj@SxQeirwscj{3lL3w0OxSS4N#ps~f16+W}Hz zQK?q@u)1Otd~MA`tIzI7mi6n+$J`sbnlJ-8$NS7juyD<*V14jzt7u4PW{n z3WFjK9m(LHrThI@bBGBL)@KcDtmhN_3t3r)*{Yl{cZ4^_ol%#=E|X>p1K*vjgYQY` z(z4(vzvy2;2}!7T%V|gQ*CkfqRw()u+{`>z?+~;8>1j#8SZln~f&qnrdtE=#*ZDHf zx52vH7j_#%%_&WrrSrT=-nn%LYeDEdQ$^}#pY9QIXrd>(mN)etrA9?j9q4h+@f=BSw$9$R~9Cv6@t# zJb7|x_T86@7WGsylObxJj>}E23aAQC!q1>2g_+`%KRn!6YHu3!eu$-QTifx$coj4=oJ8T5gr2xQK!qT_I}KAlSUKem z+lgkz7-WYhJ*bCwwQwE|e7m|}9mShH$kB81I5eVKip_~VMqEoSS|5?AJ^JE;K^b!AsWz`D?;qbL*VdOW{FpTXpQ3$8$kIl*_uj4!c}+>GT=D}K*V&^0JH9c)7ty?|&Yae^C!Yq1*{GE2I>Bu$39!Ole&lHMmNzsCnWnlQ zx6q)y^RxHX!wBTKETaPrccsOe3n{1I_G@B`>}vwTfjdzM>*=Rm$2M{xs*oGZONTC1 z$s<+`O4uM@E*8F-tBjpv;@5G|gN)ap@($jhzLto5Y{j?%oof6^rc8~;^|G?v#9R~&H@D(b+1w|8??*46u2b-On69dd8OZLu zrLg2++93SU8m`hGhG>?pDqme+qr7Yb4WroZw*(OPJ&)3oPE#KzrasHAsh-ZbKcPrF zz_T(Rd!3p#qJa`JD=$*WOBk_Dj8A9Ja2mKqy5>K6?P)*%$-Ep581)Xx*N}W*L`_Y*Joce-W08D3^@sHfG@X07q4_vzFfJ z$``%_H&c{8?S_qz%C7kVY;T6GDzjUoOc1N-!wrOVaZp)k24TAEs<3b|ZvcL=Le<&c zPh`-dx|P3+AI2}xrPI{B=&l3JE1J2*UFZL`ZP&$}rGnC=ms(G@w3E5>^{29lB@#@&yXvZXkA4K6CqO~?$uk5ns ze_Xch#LUcde0m}FBf|Fyrg6>1W}H2vZ`3jQB&ULYOG5?|7a@C>-pekv`I8r%h%tWZ zUuw!n;sZFwF+kdbro_b&!WEa~ph@E^^TnQ#R^6sx0T>*mXgVG&y&4mj8bgl=j{Ec! zHf#B%EzEdp+)6(LmLG*aBXrW8r7rgJ`SV(ZMCZ8vgv(@yLcl&~Hn>+9s`(fdY91Bg z9cB?Pe<$i4JE!ZDhlE)#Ch z*}{#AT-^ez*B?*Z$_~gAHbG-C&&?$+X;Ct1*{f7=JMjcEeQy<{p>sD0!PIQc9^w9) z)lXLfZ;ulpHMk9L?5W^hO5dJVWlIT3aga$Pb7Cb>PfneG>fB*GIXwPJ-&@zqF&i~0 z9anfzHkTx>x$@H3ddO@j5Li1w6i=?wj?1({Kk(+oI=+gr=)+=h&J;P9Cl}zFydGBa z2b4ubXxa&pmD}y{mmShZqaXDK?d2PTm)l=HXBRKMwK@*CunW zgHFwYSNPsUeoI(6EQ<^Lk zzbw)Ww43p!K&Bxro*mIR73Z7hNiJO@`$l2$=@91S0tj15kRscvBfPrTIErZ&{qPPa zp<8B-%cWQW({8D1TaiooaYcpork%^$@M;3_X4!V`ly7DKZZ7Gd%bW_tnjqzoHMn|y z!fJq*)I%w|{rQ;=k~&KLMC5^@mVp&fVHFkF8eFt^`{&`Pq69EG7xO>_VP8Wa`YEjR z^faQSxj$IRJr4sND#weod$DLWX@wfAPxZ3XxWMxYJ85=dmW>()Zdu8uG_>+Vyv6;( z8c8^&%sN~(og+pe^*clpwd5ZA36@&E(Us;H5;wW_^(RdXzXIR8=$9m* zd+YhA{Ig~Y|4<!AI}ReRrmn}LsGmN)Mnp}sb8tKZEUpxHdb<0v1@T(CGld;P-tcILX`ohHg`Eh|s9 zFI*}8igBwg{NQ7=o=Q%|@Zr0-wfl4R_cYKmTlSG%&sNcJ>&UbA?2Mu|K2>0(4<{*~ z)$cXK)i(k~;1b5(7$d{mLOWIy^KT1K{fkExq$Wg~`#c~%pmUo>+~1wZ(K#SVsWA6;_}HUucKbScJnOL4%uUEmB;k!5 zD8xB)U5LrHgmb;ibK;Uqeuy=fWZHn%C>z~m%Ol-@R{$l9MIgW0tW!2+%hPHtIxgO2 z(1NpT6rXGjJm6P^-Enck%gQFpTf;U*D_q4+Amyg5w=KZjr&BvMQMA>RA|2;oz3$oX zkaWO#JKxtte1GiBA)n@;S*C2QaF<0n0$V*>4m--{W#5j4jfeT_tWZdgz)xf$o!ZBf z?ii1GH%H6f^Ae7IdK$NoEsZb|+A+Mnzut*Yn-!jyE6!)YtY>XC-Q=cRw%RHLw=9_{ z{X!TWl@LRZyHhuz4|i4OS+=NHh*FqhYru2TKXz2!6?EK=Ru(zeVi!y1ctjJJiADN*c`V$!%B3|^z%5b@?yDOuXz(AD-qifmr}O0IYF##;LooLNwI zS~Ka2LFZAPI?(KJ*>}ZGO2ou5HGqDQT7oUEmU}z|`1&HvDMEP81NWj76K*pH<|O!6 zj(q{=Qpe5j4TG$j6ABw{V(C z*VLI}jpQ}YK^OGLJ*SUzR}Fk2ia$iT+42+UB{K$CK-GsDdA6^tyh@O5zaLy|N;xAu zq7m9G!rZz$MCx5?Qi^?D=8m~Dsfyu2WRsLY6m)8E@vR)~4CPNUVSI+1u3oB(y%x;BBgGrN)a$~-Gxr6; zQ(GSqGqa`hMf^Bf_ma%Rv?f`Uf4*^-K3uKW!yxvj@O}3029t5OU5E_vBV0FD`Rx|P zJZWVS>@^)=Cy#sgS+u{)^IGtbH_*_)ZtqWS$+=Cu6~8@_(go$&z<1h_d%8dJR%Ut| zQHgSzF!HsnF$r@9R%Z9jiQ~zxMT)-6J z(R$1Kms``{4y!S0q^7|A?aZce8rCa%Mc1Ua9FBqSA)yCXjm7z8dWZS= zGgVq|uS8!|F@4o&AqiI$EH_TV_*jM#WZx8__$(45k-Gw z6{AqQ7A$iLlJH+w48=1MVpc4hN2M^wPjOi$lCiICF`Hs;z~WX;ce36&jl-Ij2B$~o znAdSQjMMENZ6Heak^Pl;rve(ywf!Yf${Mj=2_#!?fXgv9%Pn2`rE#-ng#muYJ zFZ(@r*_(FF2{ZbYO8yf4OVdN!4AF+-g=bMP-VTs&#C;TZn1IET5n-4 zI<#r={dZW#PLA%D)QJw^L+>`Ew}Y2yT`lF zsM-#E-8eW?zHCYgxM`MkC2u8b7*teqfyc+9O&b&v8$e2l_PeiaS-<9zW@Tkk^Sjh+ zFQk2c)Bdgi`B9H5QRch3lb+d z-djIuI5w8Ia19;1dtA{QW*#2YY|Y0jx0b*panLdg7S1T<73NCs5)P*ynI##1sf+iG z>y|W%|6!mL^MuJl|9&n>LaJ|SQf%0eH^Tt38ed;&?3HGmMzNy2WHe0sP%0k%rFnSF zJe!4O^KB2kdC^&+vNZY{(KIt#_cIA1BniBu+rGBHV`+OxNGa0hQqQb{niZ>%LmW;? zREzs(av{W6Y$LbKQ?{Z(WVt?TyIxU-XeL`%(@OlbW3CelVaK%|f=sw>Ca zqj#&4Oulbh$j#wfdnKJth;U*Tkgx9cKGmvKHc#v03}e}vRYCay&;25$tV?e6?$iC= zYvE;}Sig-?mID@wj)N1upe$d$8t+A9w)iRPV= z%-8`^v?I@Yk>y|hC7MNEMurO(r$bHll$lwP$NFqyy5mjadGp`B6N=Xu$wt=uW;02N zR>Jg!eTgpJm!0p58S|?+eP|rrevEn?8~Qj6Y%lBEiSzm=CgH%71Q3^2SrxPhdRm!o z>%<>2I@QUap6hX?Fimh}{toEwrS7jy;xh?X`+kG@NQV_Dq z594C>iWS8k>z4qTlL8oZOJZR-aWz2-*o>kUopxh@2;un56gI2U7Y7(LX33cjX4P!H zSOw1SW?Lg1n#d^oC`Pkgb_R}V%1sT8>#8$}-(*m%?e7D)T1{L{%x_jD(6}pP6 z6liX~$~ENSb$aBar(YzDo?Xb!)qb{W6$DOBA2Q z>s_5hms^#Sv`^$pwVs=Y^Soqvc{hbO`7U`yHuUVU$xX1q=?ipc<}NGt_Cs2S{a!<% zw{PLzWCKjuyo0f8F?_0h>5as%K~1rqF1#~`lbrJlj6|(D$}wA2mgwWnKdXb~Dxe3g zk%{F@iT^q)#UwG?J3G52CC~{33r+3`004~Td79;TPeI8*T)zVBtAC)Ztla_%b{)rY zvfE&~VHdsC*P)<*+Ik_OOWR>1bDlI3PT4uL@^gj6eQz}p_=c&0%@MKPRo!?w`o545 z+*WqJX=ci6v}J#YDoiV9TsP$aAD~bDu6@)yJMp{%Hzzy0w^wZ;!O~^YfjLn26taYS zMh5FmKD-EG)q&;unwd~y8J_K&lNYgb_r36L2{ab4?>-GunurK(^V8`HE*Pmu(sLjGNW7e8SJhny@!wN^pZ@Z|xv!b;Hk% z`YUSc%mL|~HFUwA#qMb1;W5j2LoDZ(H3$EuyK)jKm$e6*Xyw>v6S+#?G>4rHtI_S} z@}43TiAQD~v2F9LTy~Z2Ek;Va|y#iq@(Am=3gCrYs(z>%Uy~xN|W@XP(X)d=2 zlaP8tvD!i&h>e5WK=i>kjitJ!jV^!LrpEN+@13T?Kc%mt?;~!S@+O9MrjGH;@&khS z*5rk2U8C~d?PyS+hGxmlWsEc1^_!02tl}6)s~P9ng`^mObtQt4m7KO@z^m`>p^JMb z6DJ)<%O}nO7Rp>2sK0JF`KT!=!Yy4i1{G9g{%^1ehsPn z)xLg}hdv_Qo)CR>as(-H5)Qs2JsZN#*fFg+B{`?@u;MbiqNym`X`qr5UjLZT(c*nU za;!&%EMoNK-Kdi85=QGV2k5H9N@#|IF3i}#H#G}geV~z2>X|R5UgOl%3;P)1?4#mU zVy+L;Ixze%ID~5#tM96w>;}wVl<6$};5yk@Ej!Aw(r1hFU?z&r0Te*Jr5h`$T)%J6 z=OM?B{d)Dih|~a&dwS!GpA&dHKX^iaT^Y3M0s7>4s)ohDKglWDky4%` zVbrcyOB$3s%sC)U!EEBoZN}?2w6Z@RE|K?qFjNNLhlM&42==Ko$#?~_vx1Y9-177^ zl%jrr8X)TX>dPt?H8Bs-W82Tm05D>X8m30d2*urk-f zy7`Bz25-x=sQksn*9oo*)cgmBqt8+NDyP0Yd-!mP!>(OzB+rR;;);g3HZ2VOdu`2C zUMiF74k0VnW|wvWH-)r;YyW-tOd=$n$recCb{sCFRcw?$BU|Xg)6FL42H@j_Jo)8L zTXll~gDcx-5)y?T04V{SNSfgAaoqg|O{FcbkDis&K%F{V(tM#gMTqN2-+VYTp=$6x z{>bIop@=#eo5=8s7vF0{X*!Ihdc8i5aJRjv7W2>edfsN;U{%PZSFh04X3_ImjqyI1 z?N}re+4!+Q<^>6fj|FRZ_@YA4Tzw4WoeEqz9``1%!Qw-!e@IdhV-fxJb>mF*)pSZE z714u7IIqrFkr>Xyd}3v08n?oqClG6w4I{lgF$v6D-V&LKswg6sPepbYf~(?f_3u&} z%eRSRaEZUz+)@PW4haV@>}tlPo8)UklT6>&;XjV<#cqtT%DU`9$?sA3wN}-+opS^# zJ^R9R1un>$Dt_3~#vOeC7She#ce&@QW_ z6f5AcCG*NSJE7#&#cny*3uy8C=eXTQ<=I{B(Pts%8|5b_@XRlY?CeK{kot|-vdq=q zP|@O=U=G4}cWVw0?@g6K@}9pBkQa`k?4HT(nr=1i@)qJKe0$KdpX{Xty8*&uj~X`z;F?WMc--M8)39NGr!DHD~JWE;*2zE7Oqc5@8XZ(0lZwcMNS4oZzTXA+sR<{ zTe*pp1qrNQ|CMvVt&r4xzx0d*&Rl|Ko}Yf`&*}Vac7OS5^Bhe?_pt0@Z&`VKYqkO! zS*#~0uUVwM-<4*$l7bK^9aNO}PjNO}U=W0EMwC?avc#dN#dxDH)ULLmzsZ;{C?T^Rv8ZCGkD4LE zy6bZE&86J^bfVlEE`Y&ybO{hIQJBVG+pqxjPYE2W`dZ*C6e9a^ExV4GxCZ!!?reio zN#o(jTdgv~oVPRh;H%d#xvW~LkqT+-I?uzW=98swB0L(kGJK@vW8Epi{a&LNUFS8% zAdno;fV~LPu;f2HQfvF=P!Fg*4iMV!=CWLmQWlXdZt;8-@zwqM68okDuH~gS(j}<< zgJsd`B|y2cKFq7#hnVGjk(rgXvXPe>!EHIw3pC56=HZbNaN1#-(08i>sKWBQYv+qU zgoVYOuDQmt=rP=|C8nppHV4uT0u8sHP)Y040XQ~U01@vFOJB7?i>KX2PF(llMp$o{ zXkqNiT+jZPYQ@TX+`CeOS8rbC4Y+_pFAfi&nN^g;+!jv(iXrav`%%lGTdFIMcP2zF z#$*bPzeAu+6fIY01tbE_GR@j&e((MK@TS7>{>!5NN<)36fy7MDvlGf*Za+1VNz*-E zvS1cNSpYOVtmLu9i@VpPl9`qH`S1|Mon7;j92$Q-O?X7a8)PHv2Urhm!PgfR`iE5~ z2OV}@DK$DiqB}Ei5Ec)SsOLNP%Ol=nhKPQJ(;r>m#V{8*nQUn*hpQ0-Ix5 zbyyH{UL|HjVRKyTxt^D20OOAQ>Tb&wPuDgpKrB_w8)P!=viBuG@z#(DBZXOaIHv9l zSH(yow`CC&lfd&Sz@$@$XJ+Iy36yNFZzX}7BiwdVp7^4tfWZ4_dSQa@C*ROI6_u5F z#ra#bN`U4)-Cwaqsew)_tQ>3pSj0tJOAV`{vs8bD6ig=BC$X z&v=1AvWa)>$uI>P5%MbD5Jjl1(>w?b)4ZoJ$+})#iGS4?_Z}|u4e>;O;!!0Y7nj4P z1P&1q11uTh7$G72qy3&n=}=kMaWrE|V_UCGrS+V^@U2*7#a3loMPI#}0}M~|B|ziA z_yFhx(YyvdqO@EhN zn6eo=!&>_6?5yl{hK#y8I=N z7FUR5U`NOr;qr#?%lc1`|IVv2G4EKNOiI9t0=+0Jk;UwT^HLM(JP_m^U;m0#w-FO-+ zapI!~?nmG1>~`vWCHu^pPQJfQ;QL`cVFncI#5C*>(c#k0QYDgZ0G2SO0!l`$efRD0 zThqOvTT$@!eom+jT-BM9hi4r1n-CZcYig#FBq7$kVKr7qQS4P{v^wh56cN$WuXkQ(r{t@BJC>U?MZgK zqi+*(&kQpg4+a|m_|(R&qVastP2HbZFJmfj`!%J;?Fu$fukq=6R#*`QA>yb$`7QFR z?Qx(3joa{1u{aOV58wfFQf^LLqZPREN3Mg=Krmzq6o)^>47e98bt{I9s)N!&A{<}D zmx~Go9i_1zoXtiX>NH4@hlz?gmBMvP z&eyE@e8;WN_~OJC2M5Ox%yN8*Snza;5gfgz1Y~0!E4dbZ9jMMra~Vg~V6HiH+8i66 z-*(+KRN%G*F0H20xuPO+RY%k5m918~w}2FBEMnf?=_*UfmaI1TN5*0b%};D{cpggb z_Q7KUa6^o(`I?WR-EVG$*w5X8{gVp-uwdyx_18CKA3E=Xj)_*;Uv3Nt=QWEKEsMaP zm!FStKCvA1Jj?6oVtDX0aXjnp{a#9U>DPd|sjrbWWn!|DyxjJ4*qv9@u73N*%AjVa z1o;vmKNQ`9jOHo|p@*(Z4Tvq9UgasGyqMKV6Yf><2DhD*zSt*vI(X`ZbTGVVUTsct0DkG>YZQd@5* zKOg5NjK1rIeO$dtJ0zn6FgE0fqd<|gcf@5r*R_o%CB}@DRU+Y!1m>7tBQ6|J|>oGJzkzv!l`}Ionn4O`c5%Qcz_%p#^Faur~vP1c~_exB%}% zkDlt$Tyk_YwU(CFxPJ_zdhZ9XEBfi4LRoovNYTVg16P(E=1$b6Sr-_b!)3o|KdEOg z3%(Mi94StFKD5NeOm4z^d4KyglUqujlVGU~O8gTlDq)AR@o)L6(iRiDhZ2gx9L`TQkvpR^!DUIb^A^B3)N^$qXtp(m0Kx^Rr#@@DG(nyw|sn zdY@(&dUATcZ9x2OOMda~zu(7yIWv9a9^DyYqO7Z{i*df=e+=>wZa~zsu=0#UcnBMO zNflK2vJ_t0MSfeg;po+Mn!5uq*cJgapBkQkQ&JE;e&Y__Pn-JrnnNWf(f{pWqj&lIx% z>ocXlLbB+KmU|u$iJHi3xi(#x^J!KdRL8Tzygj+YWPndhtom9N2nY5j7QFffQv@*! zS$6~K9kzNU3G~dGxcJtFrOD%f9*o*Z&(qxZ^EJeo0QWN1+xr$j!1rplH)NiN^>kvx zY;S}rOrhe~YBaa%hSs_n(1gJQI^ zs>a4%$+ELr?I68LJ#*Eb$aI0oa;6$ok zQ)~vdY7^Z0<UPt;6O@9R7i$0=&ksL$079_R-t1?gsA+ARkNneBCJr zrlQLR1$wL(dY)w|@6H#4zy#Xy%Rm%tp;fcPI?Q16PJ`6}}&=~-gR!fJX5hR1rNX?}Z`AhnlO9!slOb2y)2FDX+p7`~x z1GLU+Ky0Fy>bb_x@4T1Z6tvak2%k2LTV`M>2ra0XX$!|>DHQ<@H1d5w-x{oX4Enpf z*+7G2RlTXJB@Qg+^yMYZ+7=!sE!PcNTnED^Kb!(%Pp2zPOU&3_$ikEG7=`qq0J*kg z*SI47g^lpWi+!T-f-mwoorpq0)qkJneX}^PbD*>2f&9nRD}Y^A#7+AWgiy z8hmVR;Q9}aDUydsQ0~Cc?)+EV;y2O0;cX_a#1a#>-ku(S2D|!E{d6Wd_laZ$!VGW^ zlZ=0S?}$uHq}9-K2%-OSRC>5RJa?C9xL-6(R73)3c~7{=;nGWOo)EXuNmeBpw8mkx zcuO_cGO+Q-nhwr`#}pa{9x-^YE39-HwXW-gj>b405l?m#=}ni~B-KktfWmP4I6cSw z!|f>vWE2!d3aI+9ST5k(XrCW#uJWV2j1V9A*vHcH3g6=};^Gn^YPTDGPEAkWLnO$@ zH+gqOtHGp5==cOT6G;+aVTiqH9bRuj%1BS&@<=O=C8cQJtX|*OMk_OE0~9YBTv5S1%mqi2 zMx&s%IfRU%VPx=Q_K~}M!!KXy)L27897Yv+YmaS<`gI-)Iu^d2E$z9xJ^WVu?BjWZJ!NN-(3+E{=&gu5RRzg2Ha5|gs5Bd%ElgVl!qFJO z{+Pu)IY2mdXUpPZzHPYSTL2EKhs@c`05KbX9pwvuz7f}d?k%jGGh zY=`MLrP(|)V7#@Fuch1vZn?BB7f!JZNr_4_LgU|{Nc7tXq$7)~2JSVubMut05neUj zp}-C5>BK^}Y1$>!mD^%WP|7a9Gz*9iF`X56(9nRFYI=Hijj>dmGEhUF2Skjpy@%9IwT^3ZBQB z5SRPm1M~fo$reFB_J(GB*&{J`+QdG0RCeG_Q zesy1Zn8Q^=*QHMA@=(=oyG*#~bxvg^O7ZKA$w|$qe3+8UVVN{0>dqrK4r^kd?=pyE zwUPd7fC2ptpj&R1#0!p9P#r9AIWV#Fq3_~x+F{IufoqgSPhSBTmx%{}#l0B;Kufo% zqhc*L+Bwb_K-se#lcM8B=^J&i5cK;r)U@^Q{J8ekd~WY>CcLwL%2r%M&gC{*aIhu= z_@+SmWjB!y=M(eMeif+DLG&k zj-Oxa3_)&&mGMP7PM4!{a*6AXQlBLNa2kC7PNbDH@DZ-tiRGr?p9a`PIe_h{=$;H{ zwrOlf;EF!)Ug??nI5pP$k=V#cLphgAx*KX^fS6Nzi{ZPpqhs~k+1j3HzCI3hTO2%x z$4F0TlEY2#0dkJ09x1g6hCcOY8a!%LKJEEgG_ZgY#=SM7mH}GiU-nZZ_tpPwdu-r z^4wsa2WuY)7C5JTEOd~b0Z1fs>DF$`_0riaKoJJ8yP#ny0rZG=*6%G7xINSyFafF` z4rH}MTke_RrD(E)V|U;NrcYO7V5V$C!vz`1s$N!I678P3@#oWBp?@0?+X zM)DVA`77f5dESdXM3RfdqkPj5@xb|P!xS`G+NXe@=oLhwFCR4!&pxUR*d7mL<&SsD zkbQa|-m#d;u|wy{>1{Wrlm}dK>R`7i1NEda&?_SvJ>dBpV}h$^u}|P&q}W zEE15j0Fg;upUDDFa2#25yOW9RkjsQ@d%Ao_i#ohlh{5R382bNA_K)Fl>jPupHRpdy zge)yb!N(_~pTMa@=DtU%RcjN$1aQi@)}<*M8@~@G6_h$V+JZGF0tH^5o+Dk~QS~gb zCXHB`i*bI$S$Tz5$tzi|pF}CUD@x~MiXHdipg8iDu*k2B-v8o-x1k|3xIagKo=$=+ zE-r2d9E!a|83cU*{Rr(1tiC;2CIQ$f1^`@%Jb;QSXJ&bbI#aN*shlB1zu~+csg&iX zowuci~`}#fVxTVT0S)ufQ)Y)G@ zJPq(Rkh8F00rm7!cCi5ZA%^u}@?Zy(_5j*X1pc!ab)gkw=Ov-3;b3lJ;vvg&Z8GL3*>|RJc1#k6tc;V1;%gB zwz^ZWrKNt2>_5rT{dsFdN(!7Rt_}3tQopJcli3Qz6E>de8g0$k*6q=%dVU3~@34|?L|?91}+s?2 z)(l1BmDWS{@~vD>GVDM#P^YkV7Q`YRj~#T3lF&fd9VtlA_j% z8|$&1sF&(zh40HWm-&H_Z2OO<&VRV~^z_>Ae)%V6Ce`~Z6pDTC_ki>x-W}_2FILP> z3!W83(!gU2L73EZ4qjyw?dbnEV*T3~(`hmJ>>yRSn7?Fs^=9b_J9{dx`q!!}Q(bCy z?14CM0cj||hJ=(7#{ZhHyC#Jg=e>tqD_3}YMYDZz``kyw{#vao~`e^;?iq8$0O}ZQ^vO=p$m37$>wcVK@j_sxkDyG^N z>&GaGzs>CLUwt+y9xR#;zRNWJwp6f4a9{G0)g@BS|f!l(<+F#w^3f!|mBCtL7y*gjbb-rgBm+nCGt zHxHtsc`#W$6CqH!ReObkF5h42ME%J#`9s>c%jXZd=9!lZ4<;G5`Bd?#}`K8}id9bvHX8y&UbCwqy7s z#ZA?3F-%+I5g#z1@@8A1v<2Z=<*ZRZ{b@Qb*3~o;x7LW_RL$#*L4^o%x%&zC}5{CYT zLhL_0%_osXtW`zv8n^Z6#&-cdx?{0W5$s0diAa9`e2T}Ra%F9Wl2qK7}q$n+& z3JTIC(#_DRgwoxufP^%Rbl1?`B_c7vz);d4F?2T!d=I+zdf#uqdu{!ra^Nv>Ki7Tb zd7bAPJ}qv~&*%Ifzu4bC*jE|7>xnAUeX^gQdK^Yy(R7ioxRhA^b-?jAm^# zuyJQEz zJ%0KO2D#Szcubda32~!ET~1dyaW=3=UzLLv@6Cur(m;;$mEBJ)vk&YiE$|jMJpZ!+s2*pgU~Q$?2a-e`z@2KsJWTJ@=+~Uc1MI zx#5^`+=^juG!|2^I5%UoeVk7J+X$@Kj^5Z9qTL=_`;+&^7Zo*Z?%uBTL}M%OxfpA- zQeJ#F3xR5hNEvO{MzjAc@&cK?D*y}5^pc&I>+tHAnqu7r60)hJjo0U;f749;eNO-9 zC;e?X!pYY&oUyhmCnYSjeAo6Y+U zU*5qtPCJwfToXCKl*~sFUS7)Ey&<#*8-^WDjOA??j&T<|pQ6e(XQ>l4{(^BTj!Nr&0+ur3a1Poorjp9+#|$rjkbh?O znorkDwLDtBGMYRn(>u=n{She+p^-k{^sCeR86t!6ckER!^&ht#F3_0lL8)=JIe!?l zoonFDaG(E+z2=|OF%-1m+p3qzGt_}bKRqDe`0+n(xMqCNch5cMTMFKAzx0wiU6A-Q zgBJq``5;vK&%i;z5s;3c0*~klo=CHDqSDk7D#lZ8-t*kf#yR7UymKB3DjN2P4|}}K zwU6}>5r(FAOe14n0L1)fc6P@b(UvN&B(0pP$Ib`-hY;(ZzUYo4N?>aDgzzt$ntOZi z_U-VP0Nh@D%)R3?2i&4l@oFKVk@ZW5-}`e9T_0?*=F7YGo`7BXgkuhyjq9yV@wA^8 z^bqpwTw07b9X$S{)0%4szToK&7@AEsjS6g+Mi8-1YLtv2T9mShqb+;5ffiJWba;2$ zJ)mGU`c}YaP&!;c$5i5cAOBgU`&m)c<*eJEy-()#`>YUak3#G1r*z>3$Gdht`8Rv& znUS8d!Z-cHwkH+b5`}QD2WjEDWZ(r(RKV;V+Kn(*20GSddiq{4Ow!*4Z`{n++ZOvh z*J3L%I;zKRMr}^{oeiLg78Mmm^H`v8gRi1fiMABLUC=Uf$^d8qs%uD=-PRSdMiUPo zzvxitYA+tF+F=n$7X{5yC|5!?-G|afkui+MaZsBWu zL8JSB{P4fqpsyM296pNJh;&S}$Q%Rl&i;P%qgVRgG5YAa`q60AVLbBil2nHC)Wo%E zXfviX zBHS-HgCP4@QB3sC>Np|^-=qRFu@n=NW_;VV*x1ChpNDfl-e>y|w%w$VAr{s&E%M__ zxQk#w%71pw|I4=u3*PS{CJfM!W^ulF257vfE%ztW-)Xs!KH8ej6@40{{P>w_ar5%S z)v3D7;XY|VX% z#Z#P3&Lrq`Hz2DZ)vVk25|DMH0pOl-t*Gc(%1hGljE)8P^0?_&7;M|mFq&Q=cDq@d zDYig0u8hKqvZYv;Ikopx^%58m@&Ix)G0f`nYtx$Qx3FD6!9=WfZ7%CR%`-_nsa>xe zH?J-ynWUI#RMKCg*CDnVvQ1%I*BBtjrj=@i)aK>O_jk~tN#mRyRxMACYpk!lT+2+Y zIRSiAb=1zne9xi#zHSTsGytEZO60bDdhU645C>@fc5Va)2155?&cJ4MgHz9wS{T;EbgyVqC25ef zXePcy)|{370?+-s$^Y;CVJ}g*`8Kxkn9O*+WkWo`>Q1IypmsN3x4^_nE_oXf6c!f8 zWtqw;@^CB=X}!)hm`W?t9PN8kpRO z(D;mbn#Av@d<{a{zDu3}p$-$g@_%@~54(ycWR8=zv9W>dosZuZO&()X^A{1%23CU@HrStrm&<0sm|#EP@q?u&cRo|l^X z0LGL=Ycb7YxG)oi)fDLBLG}k_wySRI*2A=}b=>HY^D&Y1`EBpH3y-F^W#iVfWd$A; zZ>7|74-;&8BZewW+3|_sAYVP#?2>E2&`X`G#NN+>pkj!w3_yjgYOo@Ax>isE@L7;^ z^pX(OBabGH)?%a}sC{N6C8G2IEw%FYyf!|O9p4ExhQy~!VTWu=WW}ZGI=%5|FA1c) zf0n~@f;hiZ)+Zhj7;O>&;`RIsW$r6b{Ri73=5lfBi#Eos$6%hTv^(siFu zGQq9QMt5YTLytWk;1YZJ%%u12_|BNy=^7LrzI%#8m*%kCOQLeOYbd)ef!msms|qdJ zs{Smpq1tYV$-dSsmdS3~ajcsed6wWldaS$Rk(d8&h_3(5@$78t^fRmQRp+N>!x+mt zLv){vFE3z53qs!c!QwL4BIC;^9E2KYJMCrp-%|OASpoH>=Z5Kcw9;LP*4Qy1W^ke{ zJA_H*OhZQ}>A>@paF8(S?QJ7*%2MS#g6*qFgxE+=;9$sdS5U0J+aEbTa^jsTKMdDX z9*0tT&OS#>PO{r@l=OoH!CWEadDF{|bcculTyK4~RQ)Zp1g!Zfd)Rb;%HjGfJ@1VJ zi^OYo`dJQ-ujkkJT`5IHX<7>1Yzg&~LAmf`F2{0v*V3L?CMkSE=|N~@Uuw$$Pf0Ex z?}rK-L%Vx(tGU{V5#i6PMc$uf2)PB7d!8Rbf6&Sp4Wv024iQ^yL!5IV9M9S{pVL39 ztmn^+9LZ`6?61I$0*Y-n6}qBV|e6^rHv$ioAK{-fRj3G#Pl)!EXq1jxZY zujVy{NNRs^Au*E)0DulU$!o#XukzZ%B;j=^wRcGu^*8#uW4JusmwX-{n)*Zv*g%-m zfo!sasH8Cp9M;D(6DutzzwuP(?7bUE^W{I&xHA}=>M$pT0RbM9mazY-=$0kZ(;{A} z;fp(B9##mI^z@HP>@o(21+;k+9alK@$?xC)K3b9El_JvJ`;N3nV*Pd6Mcpttkgk>E z?ZhA6=RI3Y5#r)fTkbpWO={e3c31KDFT4-TUG^q?E|=E_GdmZD6ojhe<2Q5d5mFwa zdwU|DdTX25cj}xT@*0(LS}eF-ygom>oOyImEE~_Sfm0@QJ-A%%yIT7su{kJ_ znCJy;6@!JYE|^C?g9^qZm6fA-j=CO)XYo4c>wD=o$p09QGQRZk0usj%tK5pDLjjBL z5u`eBviA1&WMqMQ%ac6S-2TI5GJ)CjrWjV0Z{uJxG!E7@Q88ma+ot8jNrG7 zsOzIh6Q(aKkBhX0BJHUAW>{;cUPHz=rs`z4tjD9)HnyhSdyko7nL}_s)td8uw)Y}r zE6cxfop8{|H*Ck?*wCw!Hb$K0O>d+-Wv7p z?~JrG!q%Ztb!<%aJ(d;GCZgJ;+(8thc^MswJX8fAXDX~Tz z_<2>Irk*v$i~;!$WMG_&$K#}QrAL@oF?vKTOY(k2(gdJHt;{MzL`KGXb-tv|X<1=W z%$7wzU1?hpuN~;eVYcO`42Y*c^G#FA6jX$^TE?B-C66#Ksr#myZ8lXKx#EL=*LQ

QETtTCF~z;_>?-A4!Q*AlB{3h2EK71Rw;FbAIR`7X;|tM5PpDse{O~LBs7( zS`Ul4;Yu)+T;SER=TV(&FqZM7G80V}^WAB$&g5kelalE)FVDf)00+vohV8fn z<8HNL9n0nV)11UCow|*Z?^*9f16BRAI8FPFPWzs{Rh(6ofD7R=s})9Vd!LAmLe>hP6yeVacBBp-LNH z%|3=%`RV=s4)tT6EKd!Ct(T9;8cfz+yJ1!U=$aRuE0l`%w)&qss_>LHwOAyUU-Gr6 zRA~_+(r;qmW?{9zP)M4^ymN=sJsTP$?+6j~$JzsqDL%ViIk_uSf)Ck?mf8d zx({DT$EYw=gXKsn%Ord9+vlua=rmOCnO&*#KUy(l2jq1Kz06qb5nd*7wC>15lyZ~4 zhn)4Aw?GyANi&u;OrV0#t%~2%#jIea%LYMsw7_bccA^51IAcG2rXhIsy7_t?Zf^tt zK2^Z@ljB!cLWIvyof=}c`IG^UNt&SZqnugTQs_c259Xuq@{mw{Y9}FYFz3b}_-Xyt zlyT#Tz01e+LcZoF5*PvS0`h~yjE;T@C$a_pl_kEB0uXV-nQ@~Pb6kn6`Wsk^z%_i? zzIi6Oo6xbaAH<~2)i61H`dA`dE0F!WiE-5vUCk~t?rE3IszkGO#kQ2Bw|vf9F?o6R zCTowZ?YuW>IWs_SAu>W-wb+|spcMc>5O1E&ynP&5h=iywsa*`M+ScFVQ^2n3NP?7BCKhCo; zPta~jjf+%#C0cYnp||Z+<9W7RCDZUE^S6CvUmb>sq=PPhZYLhARV6SJOZ)7VaIu?xm+38sD?COOQOF)WY}?fM*$U@SNI}~d6+KB8%%;0GRpaC!{O$nbO<~Cz zxj^b94?}YDjWP2A+IBb>S)uEjxy1f<&AOvL?pYSVM{(PGJhSo}pK&$SWz=h|wV$cv zyS(i74I0YjY$?=OA5{p}gm(RYYp-;4|Lhj2n`pyW?H8e~5y21jGz;Ba*U zH~%rP9!hW8pAs8aloC-!5f-U!-PG~HM?Qt0_I)6BWC=_?mIQKSJ}ikDxq=B%FQEeh zGlzX|(sOP9V2^AWuzW^InfDcnW}{J1C@IgGpZm_D56%tMMj^yz?ybwr`&C5~F`>m$MHhm z>6idCw_dvWrV(_~QwSZ(My{if+;&Gr-I`e;)-PZ$|mxk4*l$Cd@dpn_v`UR^gz zLr8xS$A?IYW+va~#j$>CV!s0dprGx&_bI_4b+KQc-KJI1WXMd5SbB5V4NFhAM`UDs zy$=|DoZCS5^!4XTfbJ$~Y{cSQ_?4ud+o~~-(?)Hj*BS|9)|Z!59b&5Yj9To&1lbSc z;#eu1y_~0AkyYx(KoUy#B%@$p1xWJ67l-pzadf%yS=-Ld^6Rz$Zy+dj!y@xlGrQ$v757MF0L~+-vrI5{1%!6G z7?snz@M;LnsoZUIxi9*r*je*URMae`Obkws53g_qXx#Fv@d0v5eI!Pf&HNWQB3lnY zLr!9MYSQaJ8qoiOLmo5hbtQJ|?n9RL032|SSw07Ht7u^8Jua6jRmuI)dlg0cSArxj zcAk?!&PueJzIphKC>yo;`z>`v#$>N-Su5(Nc%9{|z5#N(YRtT={ zeB0%?%3+RUJv-xhtXuA|`uZJ!UZ(TOD7dR>n9c<8>jR{-Sq0pk6u|iw{+$5BgP=1&$~xjliJ-fO#M~ z`3Kf<<8Xw-5^(f~YtxMT%U;yicyD_xz%`KLQ6u$N>Fr1Aw16g*z4@5&<^(T+<7U;^ zXP&iL=aP!O=zYI;PQVV^o1~(4wXV)Hud*VXkE`cibqdyuxig7+(pHFmT|2d={Q|Qw zj!r-y(sV|5qAq27K+i-rl?lLlCt}7Xe+sq>@b8IXWCaAo20p+X@`3||< z&>_yTWjog@!sE7)r^=wO#|i~akaTsssP1W!p;@efkHVOpPa7SVD$}WSJtfttGIZ;GE}sq)ngIcH(Wx9*GLb^C>GtvH zmPxQbekDUxYH_fq=Y3Hok43<6<1XrL7wWXjX`bd26&@ZpANr=~^Y50*c000+ZW1?P zor~ouXaRQPcjldb(@(EOMRnQ}O)1cp0k7V#|8mce3bx5MYCoKZyBKK_o{(JxKN^vV z1|Ys)NZGV=LUp{6hwmC(Hl0CKvaIvZ+xWdJmc#E6?Mz}`RJ&=AUX*MN$xyB|^6WU?(;E4hjZNO`eA9Wn_FXj4 z>r%cPua&y+@H9SLi#2)2ZtXZAQ?Q=u zgT0gIL%*4S`Fb!L$Vf}ueOHYI*3i|Jg#!(4HSgy10!^PyO9ED{m)8yJl(!@BO;>AP zmyPeXb;vM26@k40s_p!Nk?8!eU4ZD5pZFtBqdtz48D&_WMmPqc%g)pM7Y2o$NP8Ix zRI$1xIU9=K9GDId3aXfTe+qNM{qME5nHQ&sTjM*&vv@>AbU@J=neg09vOtX$SkC*} zcYB+UfW%cfLFnvrlA!0S@ek5t#q2JgBedJ+jB`O1`mI#--sgH@xNqry?ASQPRGB}TZTSqKU$8_uDXf!S7fD)aX%{^l2R+^h1>Yw4FK)VbCn| zU|uX5m*wWE9Asc1D1Dk{d5#>``3G8e)J_?fm$+X_4 z4*fQ^u9a=^PmR8aTr^Z^FgVIup15q1zj^yR1rfB&-wgZrXzeb4;Op7V%gp~mci4f2 zBNMlyM9I!vy;N`_RibBNITJ7c%(kF3fNgPz*pvJO5b(1YR7kDn)`xgcpQQ-B6pbSF zENJ>FL3;d0b_%7uel1FS%%88-$Zk+EnSJ3C{#CC!v=evXBR?(1teYNbP!M@|`tOkq z5kuGCQS0;R&#MNsX{^(!m7NAG?t4>&r!pEsZ}B`^6$n57ox=mhPhIa&8rtW(R?VLX z{c_}|uPF1hEHPJ)eG#zCOHOyI`DZ~Vm)d`1SzRJ14Sgoiw$=D+Br*Ue^q@<-Y|y;8 zT*f=l;Db@f`UgO6M{%{|0 zBp1qhOs9v_G^mCLOFneL{yikiH|ZYblNfwR@6U)8v5juwb*X%ry!x6MZ)3NDj*Oqd zu-(mdDmCnH@7zjT;P)>sfPV^d!t%Gjli^g!WBto6IaufP%L`-N+yg#N?=K0wxvCKj z^Wu6sH1_(pfs!&p7ghB^51Z8TPl>)AY2Po7TM=J$^^PybLGs53mFae8+f^yye}Jfp z_=M_|*tZY4J?^5L^GjWn())JaRal(axD@J^Yu7$P%Au>QhEG!G5*ttNBK`H;7<^l$)0G>qBvQ2VdaVvJcJ7Y<5; z9MUfO7sBuc>NpK>6SS)LZ|pX0(ZT3L&LhJH6 z{nBg#Q*{LOs$<=p3p~9Yk1H$iqgX8IA7FlY7z`9-Wqbd=rzqQhwTn4bzIN_Qd)clx zgCD1c9m+pR`E`;1_ni9Q@=mrypXts{M7eJ0onOd{IXP7S;yTS>2lvT+3AGOY{$5hr z+|jGWKZe%D==S|lNLBjLVM=&wH*bb4PYX0*I3Fraa{jsG)RZG z!F33g{g<@DVua5$+tPU$L26H|K(o(+L(GS}7W7iPwtwVx=kjk52Jpvb{Kea;Nw9;7 z9aa9`B!snVc7X)X*&!& zcpqzyviC^-)SLn7j&@$3YsVz-+%fC09-a| zAVN_KC;i&wp`1js;9{I?PCc1+Nkpi8qwR?V(t`N|sXucU!al8vw_YUu^|2bzEt;E^ zFZ&o@v)})*J1o^1Ci8u6>yOfch)ACjIS^mkU$CCmCHx&`Egfmx1lmA%E@COk`W1gm z^qX04L&AtRCH@=hR2LF;aPTP}89uW~!O=CEu{HKy+LwV?MMT2S8?VQHPp2S^z|(d| zoXi8_FYV7*klj^utXs+c3s3!TcK_Fpl+JfvzD$WK6_cQ){*i@=y|mS^v;_n!p*ttb zh`@#MU;nME3KjB2zdJBB?lY^mWbYpP^o)sbAUW8sDIi$EAdOc+T)0M(^q(f9auGY%|@proSuDsMGuzGi-4ur;Gc{jw(v`AwCAnk5E9pP}a8B*_04TFK`T zND6*U=~OaXPg z6pL@5x~dDS6T{`$#aW^RvCb-<_PvK>TZ)St3gFXj>vexc!O}wfLpuw*j&B`Truv?W)s?g?)N{ zzO+j}e{>%(4a|EFdK9dqP5(fNw<{Kv>+s6;^K}?mBm(NfKPe#prvTTvg`ywWPIXGv z{k{3kn8nhW%fwfUTwJxh0UZDLeD2KUlZ}J_+!$vlsV> z>apcdkMPV#J=l@@o~%&VF)|?*kCZfNX&3vSP5J+CCDk^fHiM7tbXaRJzkd5B?c(AR zXALkCFhbvueEfaF^wXYfPOs|*uYDcIj!Eklx#d3O*G&OOm*(h~5)vU>Lk(jgR_6pP z8UvfQIKLm%{CcX3^1qJFP_sNdSFShtOWj4WsHo^-7@w81JGfa;aM>m3N5|0Sf)gdi zZ;IW&Vd!>6J_2JiB;0WbbYPOX^jap)6;A*7c2ky?nzo0BhgzV?3%1(&KmDuT-_clh< zac!6pU5kLFyurLSFjJZEbD+DA%Aq9(%#aPl;UJ-dC2f zG=vWMie-LN_Q%s1o$C+@lBy+UwXUS`)UIXLtKeoQ+mV)6eGlR3NPBqc{7sU(u>hV- zf=(`l8u+{6<*SsKy(K6Vq&^adn5c?%JKXBEv3Ny~OU6s1k}D5hf~t2#W=dqg9i%p! zDaaZW!>vOvQ}?Ckc?WD(fi9GS9>BXi z^K?7dpv5?%h!`zOjp!tISN#@xsyJGVl&iLyq(jFdh~OR5r)SmTX9L(_WS*OvG_RzM z{h*x%=odZAW(MOcOtY15=ueJT7&&EMW{>c6KMpEMO&wO=-m9Lpiqo%Dh}=}KIHk-P@E89(+PO>PT(HkiF)@So zX6Dj4wlZ;(5eNNW{Pnv5x|`lIV*QPI>^rMBCjmB0GCn8ie0a7||EX~JYTo5QtdG~_ zEc8#(jpGoC}!)$*mi4Ne8z8(p=QPMivn`-OjV-m8A*(ghIe9U_l+5UgqUZ2|aI ztk>RX;?3mrC;$@Mt=czenC|903W~LT1r2B!g{tebMTEheBd)o_?UzFvzgN^~>M^rU zafdX6+7fYl=X2y9CYn8ebag6&9us%n&+FYahJ1kqLh6+!s%;lu*;|t7nJHcgo-VKZ zXLPb@9mGz}rZr>U|MW&n2 z55m&ISEW_in-0o3Z5ByAyd*Fx9W?R)*dbl(v$q3$@EzY!X4&lI_kY3`cH1Qr$jlpe zix6|1>pg-)Ir2^ojbj(+clu%hY+XJR%wwYv)!}b&U0lwD3De3(1TaqF+jcnKn%;;rShoFI6I9Om2uB2eN|k;BT@Aqc;2gClorWKS$Bb4c^4G!_xd+vQQ2 zs3v$xNnI|$!Wn-K_l+-yq>eZ&r4cf#Czy#?Lw5Sgvt_=Hjj7L2@bsWXczAjmeLZjQ zeQF*G%$yICpIko9FF{pQyK}dQ4VYtspaE4+?15RU6o|uZcmbhV^hK^+qR~2M2g zeq8>%YRYY$by?-drn6ky+qN(4a@jPNE5)m{!$h9@RWWL@5pXWcjQ7fCh@~L~)5K$g zL)RZcYRbUC)b%W$>n99jsT1Hec%3X?HPXg8mYRJHx%#89Y=z!P_vB!=GRO+$#pfne znDg6hE|#p4y_W#RbmRDA#s0Fl3VOg#5Z#Z34t;+=w?nT% z$ItdH6313_pEC1^AL zB8zb|~|{X>_RmwT5V=iwPk_&Evv2dfkX zrLav7>ls8&IC-Mp0}1C^L`p+4Y$Zu`Q?*b-WTM8J{xRrz&b0ec9z#r{kF|F5867vytRRsEIjMep=?OKKFa zHC%Q}%pjZ~1%lnOkdt$hek!gRDyi;JytHos!7R*k#27**;k#}&>4i)T2^m$567G~d3>+ppBCm#LYkGRuMN zENrAX6*sVDOZQ(Q&jqlsHTTzA#Ez%z!`B`9k!fkdm__%^n3gt#N@rcCCI;KG04}eO zdbc(*Z5I~TOIn^nGe-(F#T?KxLK9fud#{pvUp&Qs0AD!Xm|j;hSedGOJJ`lDose$; z)}!v%y@P`jCqZsy7DqeTXdKeOwycq_Osqx(r@y?|XAk=L@tNmVfp^Jx{rSQf?^t|m zgcrq@mJb#-A+y@>G-KHBwm)v5%KLt5c+=NhHKJc!LS!J=FAsJ#T}aVb6*{b~Gdr}q zt%44dN0%kV}y0Tay0w9g50@PD0aY4Vs>}E0VbVG(XBs?A5 zTkN};A4W(W)7h_lz=>Qjt@%Q;s+-ey)4wm@6lOiN-f64UQbVYDtX~c}9$Zt7GyUcBj-s_Qb*L? zUV2jnfOs?-tZIt4MKF8-#q`bU)Q0zQiGs(lZ*p7pS-b$%B6F@nY8m62U0FK~Fz%{k;$f_4y zk0ASwD@TkRgqI>9FYEzKzp1x^GZrx$&C{n(mACur_ckh4)gP2E7pO8WtnsP|36xl! z&CGZrSl4apwVPtV4MY?a)^RidwD$^m3>-p>UEgzc?LdyE5#NHYeW({9ve(39L^0jwIu4?||1p{RpZY%{ z9339ue~_tH2$%PovpJsPMaxPANp-o&Z|;VlX|So;NFHZSL}bSxC$n25y1#bh(r~dc zp3|Ftlf^t)GZ2}ud6i?5#)(RPdnLPBFokF6z@u1K+qLS7nLJc_H3QrV_f0R|hq`Ac z)t%@8EWCZ?rkD^s#mrZ@x*k4(0@tttw^y%Tbs>+r?*X_$T#1~mqxnFJ?&{6SKD^hl zOzIqY@= zt}nnK@7P>c?J4zUSdRvnU zc=*wabxxLxaoSc-$1p)CvB1(n_cc2##ygGe# z`?4t<9ii*rS6$x$uw(wSw|7o~y(EA#+I9~v)5Ix|OTGiJWAb)__MSY+0zWNKD}r1c z&#D1t5__;U4YmBN$o{4lV9VsZl3UYVTUz88V&N88&Q0~EdlEy!fA*WBi|`em7ziDC z-lE4SJA?1jDs-JoGMH8o_Rlyxd$uO7_X#P6U933as=I6 zoanh!X6W)7j1NzdjiI;{qz{y*>FTbKGHcXjiaUtD#f>I4XmGr6=w8YMIz64p>OY03 zy<;bGr5kivU+D1j%&K=&P~$5sCe9J82QjbHQ~7(mq&OF8nr&J`uDNPYk+Fb4T}%xR z2?NSyqFgxuo)^qm7Nk}FGxx=LRixv0#3x^C-f&vlx(l()uxcx0R&U~Q-E~o}fas3~ z$`hNTDyKMB?PACt3sGRCBo@nH(s z%TOKbTah7l3~$nR)2xP^Bb|1m?#x7!HyE^u0)kAvvP0z^3nX%gqa+*)ft^-lx!H(W z{L?o%{DY8e)grA!zvlB}iir3M6RBD7`E8jf-PZR1GROE?jVohj-R;w$=fqDb`?i=} zgkz?GLg6{9>8Zy|YSfoqQ;((OMi^XN8L2m6KH4*sA;z$!RTO*)Z*o=22hD$(HCz|& z8*sf}&dJh0tys4J5q8}z%TU(w2h@nR!WlGAhT`v>H48qTa@{!nCcYTteut+ewV}+%byWs+hEdCj!4Kfa#t9cZm`(S-Te&d$!z)ggMGolL~$aG6*R(~almM;|enEx+&7V67cQ=K)P3e=PKO zrcJ5ST0(uo z@H&$)5ji;<<}M~64`Xe*hxONB>t7lD|7ilXx}!A$nZs-;Xk2{Tlz^z*PrUvtgb)Y* zMkmp6>iM$+FGN(au3g7v<2J0TcPSsd%cME1-rqqdrRuQPBRGlkHU1uSaV9&tjly%^ zw4YY$I6O=$bj|!Z88+{u1sgWP>aCWyJekXbu&rohT?#4YomJELhUL(UMn>1A$Lr&T zML08!&zW@uSG1%-*wVU~Qi1-0CmhOg1qP~atRkTMaBrByNRH_?dy-y_147nev?fyRg@T(&XY03gUT;a@~Y(GT!mPd7Nm21V0B<5;hx7wE!2 zmqy{>1E^t#SrmXOnu(b}h&197bqj+uR(w=_;0MC{aDBLV+YZK z&v>5q!hE)hEN|g>uo_dk|E?_+A>EqTE?NvrQPSu_t@t8Nf+PY323#tELLBthqbDKQ zt4sMF9#LbnYXf;7wb+;kSPZjM?V-&tP7MsRNM8_He-T+|=3%t1lFB#QZ($t-@(IGK z#t}Ztki_h-!zHW6?ZppB4x~Q3EO?@napv`s)jkKzI0UsFQG}Z2YvFR3iG1i_EUCt^ z-BM>qG~0z&v>Sg1x?hXg4Omi5=2((GS|*z{SFaZrmU2h7)5yLpa1vIp9ltD7b>{RM z?2ajoI7>cluOW`n9*MGYdsn)Nv9LJi{l@4}zuS;n7rjgW)LmJQaYUSB*LW$ld#qmG ze{!piZGa`nY0jlFhA~BFml0pZ`1^B=asv{*@Mt|2#gdt14(ly?lX3PCkwE|80$Rvf z3tV9tVqax8`XqO>SliUm*2+#|h~RCBfvQi8w;C0C%U>5S@Q;rX`Zk}uPMNY1BLjWw zg|Q^&w2Sikr~-*LK#{zn3XNRN?z)Cu`zfQ}xz()=nUJ420uaN=GMTH{-3~7FmJOF@ z>&Ck^vt7Wh0SzJ}Gue#4PUBj=bY zQ-|~BHv@{U64YW}k}=Z*8vcceROB$~1k6mWifY~6>U(t~pT;W(CaU+7;CoFJ8F6jp z33_(s1NI$qHm@ko%}oonTswK0Gr%3?TG5kO38fL2#a?pE++z%Bahpt3Z|Y;Ca2sNu z8B%$&srvh`6$~hP63W_f9r7J7zBzM=P-TkcTN^oWYy4d4omV=nDpLkxTQc<%m`=o_e-P}$6@ig z@Yj70*f-$Xp33$U-F9YT>|479{gjSkQUIob_Q&zfXgCSpw>M5~{r2e*e**JMbg!e0 znR%tzNmash+7RM=MY*hv@rp4t<(^n(1nK#-b4c>}meYZ<5x~E}oeEGI=r?4rbKVqLU-Fwh|94luvq*9_LU% zi-NLxC6)`Oe-l(5Izx{5Kk?=N3p|bDt-7B(5&_4#zm6q5YWI44ck9sO><#EaPZ&>y zdzg7a)`dJs9fQ}FP6>;sR7%lDwZtH$P&$L-LoT7-o4 zQD5;&9{f0YW46lbU6=pL2dFt(RV#J{TBhQk3zk?6xl45TZGICMs?bZ=Fj9?*Q5$*h zFqtDNtk$4OVl-SCN%#P6SeRs1#eB+lQ;x^+t^hkQ;Lc*R|2>Z`fQ}VIEtWCW1P_3m z50s5ho9b=_!0!a>1pD$$%EYttb#4j8Q3`w`i}n{ve(Foi3^Z)h#hwaX*vlV5?nO4J zWt4`8ZYcv7L{d}J+@6WiKKa=Vu%EPS-Pn2f%Uo%a{Ny&35_%XZfmT+Mp( zMS+&LQ)fZBs@rwIxbyJB7HrF=TW-vqiRg;ElaxxcL{G_QYRvY#ZpzOoYL^lUS{7i%n^nz@$Sm%pg0tENeZFy%IO_VKM3f6HL~ zm(GVU8*!G85 zG4kB5e5a5Ub5}~}Rpxt;8ndI96em(wTZ?tVnDNE>=c?)ZarV&9nqytPqf6DCoS}e} zq)j&3R-v}~^-N>RW#O*+8c{9VKjSR^nvzqakYU-?e24=&Tw_b!<@MO{ICl;c?f7B! z7gYkZmCD-*t*VAp>k=HItTJ&*8j^wUy(S1O9l0kV#ur&{7O3IrO7{`F-Z7y1zWE3h zB}PJL_$C;tFr{4KvyXGAY$dhIuUrVh$ztZ#+*AYG0tNBcV-MP#&);bIhKBsdfO_j-Ev+{4DJ;;_^4V%YV<{|G^phdQi@NN~c<{ z(j18?+}^6@HK_E^KsPOw4tHG{dB1uq(4n-;O!F?i=0b(NcC(Q*dQzYJ0?kvoR&flK zSL`1h+@;8NTpitN8=oxaVaF3wnP16ZwrboAzLon`QWWb{Bd|}6y&aTi&{HNP(NF))`Kz4SvjBG;oEPL;HjN~RFdt~pGmAxG;n`3iq*^ZTUa2$S@?)(0H zKi|){`;*^4J?i9ryx;HZx?c17dOoe~^E16S`m5BC*qxEnne&72d1MX9>_~3NXzt6a zvssLM{Me1`XtD!4`(G3zwYk5l(?XwXB~0i& zTXQLxdL|Qg!KdEILRlQUlJRcSQrxNxFQaln1{BXmU#B1YkJkfJ!H6`0kJB>&)MN)v zfzMgim-cNeKP*x0>RchoV3^tW4FAx}S*0yq`(fMvMaK4rqket{GpAP7jn=y4Eq00u z-skmrBeO(*Jw^CcESHTj(~Wx-9t%2lJbzD_{QEQhU4}l?pOHD4UMw=Mc3PmMEN+*( zM5Q?072_%76^72Ca8;}AgSq8@y3;8KOQzgead-HzQqk(Idc}0y*X;`KN9jEW?~IF2 zQmWi43*k+MSIX3xgBB6@EgGQDAI>r|xXvpD3nUuFPB18qN&-`Gh1H!?Y-6ng5&q$k z)4eAO5;W3mdpabthcXMw8-C*EsUpy*i>)y$eQrUa@{uwYI^DP@-=nU&TSGx zPTrS3{M*O4%go4}3XkpFxXJU+yyGI=m*R?x~n;2A^D> zkAAgOndh)d@cy^#%76Zrwv1PmXC7n4#CosESz)u@vQ9K&qoi7`KAJ&dn8NCM+xpp% z1M2qoli%Ie&zKfaaWUR90#q^U!VH(6=V~=yk`2~e()ANP!u-RZ-DuHBh-l0CF~#RH zplbMT`{A!P#D9L~mi~$We}$%_@WYb?k`BK~)6-8uz+KdS+D zn;v!AMd9M~I|Y%?Ji-*7JrpDhU2?_H|8R8ysou5o`<5k8ch|)nCNlO&tm+6Sd0198 zMe}9gUN{rD2XCVDy_{*>Wdp+5G*6U=`$r~RFtZ?e^9?)RzZUj?Wd*vw_CPcP%fG-LNzU_EOt?TMu7Q!% z{8y8*sfn$>7kK~=+j_~#KRrA=W06lXl!}fsdhWXpxC?>n)Q{$ah`fHax&HIBwhR%e z(tEU=H}0fT;}B5AZV^Y&KAyLGT=w|>*#v~IEb&%j#carV?&gATFreoQw*ibb60C|j z!iV|m6P2vOf&{SFB28x-bW@-0+Bu-6;kU8mvhR{myakh507kwTb$cTi0=CB{@m3|= zY6>|hz;>Q(p)S?@{5GGy{W9((W*p!IWF`FwIo#PfIM2HNxvgRV-oM0SQh@0^zYcuu ztLm$Sx3>3FFQokB#sBR=ClqNez`Bg*K7?3&ao*F>p!=#hy!sSt>-fCSp+_TP&{m+w zbM3tLWai8nG@$8TE&ZOh=+Eu6#Q@;adV6*58t2L`!CKma{V=~Sj3MyXDBN!!=9T)D z13n6ZQn;1gUhm&4GR5){pz>|$X8s(``_H}oKPF{M3;5oL{~8%EA?Chd zxs-n1OBz(q>{`Pf+}-B9ON7;+0LH!akR9#@=e@-C=9$}%y!`?1+t;svw5|Wrrs%Y` zfx*nBR^DIP-+%Zlo^-4GI6R50uUHpyhz{Gcp}U5Q8_jOqA@%JoNw#!U%eLJBEzyk| z^Hr?p#)Jc7>4lN_VRfzzpClj!&6W#l3Oo+cxXseBBSHQ8T&l{y5XgA19ZhFQE0u(k`Uu8Nn*yZ=TJ^9xb4ToVlbVnK$7mEcn z(3uy^i$8tZ!JsN(+LNSc1e(D)s4(G^2VST%NHuK@f$4c|7t|j*$C1I6l0~iX zjB7=7(dS3}gqOPDQg@L?sj>+yW$w6#;GA|8JWm$u3}S9B>IAOiMoUdClB1B!l*|XE z28@8hj&F!*6(UVj+_m~4Hye}S84}YSp#Rj7{^iXXx?QBRvDFER6OUDl=uh2Mile5;)D7($j}j-c2?_swo6+(Uz8$_RJko z6`}@l0QiCdAmr>)On-{ohKp9+E8Db;eU|QDX2OgbMOKQIsb=J@Nk5d>_tL*# z24KS(WhT>CNLESQPhow$jvLAR9Ih>0@8eM@AECLmabyytCuC=0DFR>-xLCYxS4YwOk~)q76!w|HcH zUH31vr4*yI067R5n`d5@Tzo${x`*FyA?gb8x~8WK1i4C2$zO%!eHZa_^HyVVJ~R8# zzd%KCh0AQYxkR#Xt}BYazV!$DKbVmJvC6*p32%_T_Rsk21rtP`brq5%MoPwKcE<(z zU5+0n*EcL*XNul9LD5J@YHP-D-iFQ7s~FF>A=e}_1{>XeL~`2pb==pG^7DwKQ(P>x zMO3Z#r;E`6l0Ba!`;8RP#_H6*h;xW{v;WXZf22^85uY5ZbD=YCGPwU|0BUy|v+1?J zlQX$U*1+%Oendoxr80aU{@^mpbNlg<-E!Cpk(4Ch-aj`%!RI73R$)yCkb7}lO;73?YK2_*K&|UF^-4Ki%%+u=$dvh^teo0&!;isNt<4zv6)B9lp-$Pv0j68&n%o>1zHG$>xA1@`0Cu-Q# zdLsm3Vzg+}|Ag9bns@~n;*s(yNk<$zBPHeTv{&3Rpi=FbYe6_E| ztdb+Q-Nm6On^0kHw7blvTw`~)J&rY%OCGf+c`#wfE0)^SgvfAHy3j zKM||K(*2!pm3oMIH{ap=>~(XO2lba~%T6eGJ)Sp>*L5fCHeXzk4@gyChYQptSm((M zF$~uTpB#uaO;xlTdWc`FLjyHx-g10OzOVN++EUUM2FBv?BwbKz<*+q9%{r@El>j+E zpDE9BC`b3?KoLT=%o9$806L~ilTph*L%+Z*5sgoxu%QokkLD&HDlRgAe)KyN{?*ji zE&0VV5HAfy`;l?*p@lt9{RVvY*LbfG*nG!>Ad>{hBIB2P)4#lAczZbMgrd{Q?$z_! zb5;rHLVou-afe@UVd(jr3n1RE*Q(VSn-Bc{x_;}1p~tX=*(jJnPdFNGl(}a4JAu-OGx`tM~^#BM#S(ZD-{P zxVkB=vWTm#k&gUOf2*bFf@rOvNE%UVzw1BEN*~>f?ZITz-U=$gYODfIaC2FX2oYub_cenRUp!e;c*XghiI_7r5HILlSj(bBF5 z5R6`D;0ROjtg;{h208Bm;DsJcVAVa+CFilZS1%uM*ZP`3xE_G*d_++HSRZ@*7d@he*pZ$Xi z4*_VB%97t$Mwk|`C6`wLS^2o_aUZg`9Wf9YT2J;VBN5zu>}m*->Wa<8AjQ(=FdtRd zd!>#zR=RI@SXiL%*Ch45(3KPdZf)?ePk@UKFm~AmROXrwEDrxLgT_h@Fv-@j);cUi zdzV0SGXGNPY4g<&)4+9iFqORc-z@+J7Upwm1#CPCCzk#!Y34KN6u<}j^A})abK%SczWr7{1)gUcV0De&wAeetP>F^;O-E-apvO1E?r&O z1@MEjZX*q3-->U!;(5O4W*A4rpCQHX{3xhpy&?|eAPab@b+QI`+}>do?gV3f5B%w0 zfGJ!P#q@YG*z!1CTPA*bm})A#jPFKzo8KJ7L}vqYLBLmy1}B; zW^`W$k-|K2^2LN7gt~DYrmzk)a*_^SW?Vj;{z>JeVrL1~2|_O#!xlWX-e}>@M}Jyx zgUjJB9zN>fko-v;@Xz6^h#Mvb@uM45m;-;7H7?)4zzo1#kaJA>pV;=7fBS=(P+6UE z&HerP#>aks#}#qv@7fH+D96!gD)l7CN6Be5?2wNRG@d@n+& zt~B^92L8@@HWOX`e6DHiKffANlDZwZ1phK4U&qh9%71?NoLX^XP2^WK+<*T}CujF}`<(0?$AI~est-z%~S)~d&!=2uO>C&E6gW=;TpI3%VPNNcJjZ1#LIF(hV9a#`%>O{F9FWu zp-5Qbm-9ai_1SW^4fH%Y_i}!?fFb7VvB-Pg8lms28`K65sTKKg&buyzd4ImFLBpk* z^FBG@<;Q0)Yl431)p;+$)CA5=L z?f-KzsFy*cHc+c{y7}K03IgofhKjTS19FqP2LPp z2gI#W>A^Vn8!i*+1;e?=XESd$Iz#Q%-T=|5igWx}(g*cP<-=)6af@EJHKH3a|5H~qKOxlHYw zCLTm|sdv^P@7&LURdx30sAI?7^In2MOXxJ$^6mn^5y9_M=f7TpZ!?KhN=E2ePqUkxZIpHKm4Y5`|ZE znmjdolKJUIN~XgBYrE{~VE!LO{iQ9ifPZ*NzORU+c0nRFh4=OJ51XN?sMeqq&RMar z7ubJZAQgy-<1P4-QY~cyPW*lL8_KX>Bm(4p^AwUz4er?xAE?}sQ<>-K zmuuS-j+NQwQ7w_6sbVIW1f&i+Kn9iMEOhvFPWi)gdeg@&`xKA23JP^m{+>U+@xdES zt95D}h75V`xKTG6!D8hd=DS|h#sFm^JYj|Yi{!{8Ap); zW%7FgO^21<^kmp!hT}X;`1GFTaB*d^pT3XaLy*2#x2{G_Ima2bCrG5Q=<6}47e4@U zE8%&bv-gF(g#@T?1zz_BhYg6ISvwC?PUNK{(ma!H z3==w%${T_u^D8PO37EfwGD;J<&-$~ZF9y9^lFoa_j`^!S=ZnPcsW2duvxIeyW)Ykg~bS3!jRccGT6?m{dRuy3}l!DJ= zHh;8-h~&|KVmi6dAxINNC+`%n@`L$VkrqC2P31_!dv0s;LvM%_wf`nfhL8)OL|`}C zdIYbx)z4CkWHZD@l&h53P+q~s-G;yqb`Q5uY43kjT?)8%hu*N&-_zct@AIK@gInpi zUI`KxOe(UlIfyJ!K8b_`{st0$JG|X7`TPyCtcZ4bjUy#l*@3 zq@ctnWZneVda72a5g$(Q&hSs#E^37=9E|V z(|20iXr!<>Ori89)=Le8sfI%;?cVKRMq%v*ww_ds6uOg#Y(F!$|C0!uTz&AvXZV0W z7yE*_NI0}RrP`%v^@JsodMS~|wJAy1XNs>Xp4LFB^Uc?|szJ}-y|L3`X~4pfEgtkn zY<++Bi)2zHu@)80-Dt=a=kTBJjp$?Z_5!0(|2%!q_5Jm6ZJ8LR6}$mrS1gC64(0@U z(as`xkpV#Fm%wZsqv)4AhO`%I9hQ1%#NOcT^Xhw&=d;!Uwbt*)HVad(-6ksiI#A6r zqu8z-9T>>F+d$5}Fj{KpaIp2_(0+*pxu$kOExB%QAu6mDbu8FwXoCGcq}RN~ct4=DV-jQ8VIdPv!HUQi zabZk*g|uhsueW2`F_&zv3*t=djG-gEQkK!>t&ELp-?G&JZ&ikbqv`u!=jxcc)Sqzh zB)B!&ny4`a&7@>C&D`1QS7d|0REIQ>URYDIuS2!mz9?RPnagIPZ7H|{h?Pi{=#{id z@30p;7sT2hX^iroxntj6OjB6$PFwL0pvU+}N)Vg9D7AdTV1K%o_Tko-AKPJv=zEiB zq%26;KTlRug6arpDGnrn!s5vl*QtX?_6r>_IkJ2zhi-(1Oq{peYroslLkuxAK*{9= zRR=3{EjFyhhpCQ=Isd~tu$cW{jXscZS^V)lUx&BLI3C!2Pu+_k8Hl8#Vzp!^;EKQ{ zNx4$pH35n_Mq|5VWvk^wJqKEQfhYM7K$<)^bH=gG!w-ZK@J-z6EY@U#8xQp79 zUQ(dUGA`hDSnSfYEKN%jk7O`r@KO6D;QCC&Z=HJK(SkKEZZ^EM2;AQdb#=gmvLm26FJy*>3EA|k>9V4`z;rmOVozQ{0)dq`t zm|05w%aH!XWh{TTl)lp|X1z+(Rfn{XrN)8YiFs3_cfk#KWOk=ON04}2=eiL=ubfmV zJfp6pv@x~R{BzZx9&}4`#uukw7QhQI88ntbd8ofhj4o_C^(`qQ17j@f9=}GLwRV~a zMm&#es8MIEs`3=1L@u6P8dR{>Q~XaJ)7pK)nz4KpR{ubsEo#N#u#CnV=W6Ylbg3s8 znf?51L~&Ed%Xo9DaYJ)O5}31PQf`1j2pY$|S$6FqVJh8z>A)`=!qj}1UhfKT5WTFQ zTobKoPiaJn`(WlLa~SJ!TTPfzZtuW`VgYT877fwK!Q3@X^kITo`e^B#ARC7>g_=7{ENa#`mahOKaz7CM+aBe=pv{ohKVkKPp;Px+i)G)?J@ zW7VEhD><4Vs|MxMo;p~n@Meg9r9~x&9%N#?-{B#s$A{}RqNK7rRB3#CKQE3?hDis< z78y@BZIxVQ4$8^y1EaZjFX0M3056?oeatzWS-a^vzCc;nQvGI?hgIs|yI1NKrY&?Q zGzwoT>>;S~T_q({ue?Xrfm?FMJ>$7nXGry5+?gIPdhm#HT;ne7PAW5;;K+Q6_kSBC~tkS-!WIXc>+4|#&h`x9#zw1=A zij2A7?Uxp*0LWsYmMjHY*EdxXFFQ6u<4r}sf0cd!Od_Bg;3wYAK2hD7kcnfNTrUhT zwiOfcIqgL})+~b*_8U}}ey}42_=Yeh?VFML%08(_b`zs!aFYPMYk?K(fUpvT$SCN8~1}<|>6ED0dsLy}$OrxFfo!CU1Y4 zErgs)`NK#aO+vfO_?g@Ogg%L_yJ|Vk=AK#yTJE)>kqIcs_DAkJ**|z}Cc+&T`G$zR z*bdifqF{9Tk4pD+hOH5`0}exd_UI$Nn=QXa^@^-reJ~2D0G+rulH+zIWq&r#V#e3$uWq>7qW{diwx3DQ&?Da>zmB7vxPn90~>@bDm-6c z2DF4w(p@8=X9D72`|cWNs>`U&dPORqjKXNQgBQ}tN5is{dV=~9p_F6Voj@o^@q|+| z;7r;HkPsTZ>wM4V2ix=KDMNLZNQJJ!v*ie)@pX*o;*2tUFEV_%%*!hzPligfa^Al#hpxJNow z#bxKvm+>vbH~dW)MGW-hNS2fTMGnp#PUI#YFdRaY%l_i$$+1{Cr&pJc7b6N))ifg*Y5^eJjwYe82!QmWiU9<9}AGMvbB zs-~%|OKqLNnE}0cFmNVwm|9!5Pr}n`f-~fiib~-65R7_n3QM`K-d$!0lE$tMIsPw;SMDRp5me<0%t z4S7urouUo>Giqps*^~~IN1j~`q9!>g^J(Meb?WJI(`!KPvv<9)-%t}@fP&j8Fy^p7 zWortO*|pglQ;4b%WXUK#*{Cy^y$4#uMKMoQHiP+CK%_Tpx<;>(QDfY>sC0z(dDG7D zVd*q(vfjA*5TJuXD*2Bf?!`K_S$XuZ;bQCCwXkEGJMb!NluNu-%59IZ$2Lumw_rZm zZwGSZ?}KlPuz(3M;@=*wem!|ls~RcT;q@*6>B(SOCLo{8CvS6$)tc6;#2G4|^w?fJ zarb>O4FVYJg%DZ8lX$!kntIS&ulwSBBK}<1`l+7Kg3Ji$cU*dLxUy58uXwreiajKZ~B+ z{T7JL#$`1)+Q11n49hv|vVW~>ZuYg61&ud9fM=4@suQ%ziNKbm3IGKsR2cBjpVI zlWD$bE<>pVg*Uv)axFz{-7v%wo=7EPTkWl*5|%d~P07P{2#CMt2;cF1q$YG|{XG9?Ow%Jy9G~8xb2`L@1RrTp`tu{DyJ$+Tzr1#o`w*iksfJ| zG%KM)#-E689PXaDtn{-G&(wGFwI=g9hbvI>*O*=zD!~xjm}6Am$bX1~=bp-l+z~&W zaN^$sc1);X|)V(59cm+B!&}N}dbA^B!5M+Fb~loS1$;Vh?F zyqs58e(v-sbR_wO$61nCLz~9xHeQ98*)gs!@fs>5m=jK+CSg$HW__iIN}%bo7xdVz zV842ZJ9fn}eLTsoAB%6OV7ixqWFF?Sx3J_Lfo;cSd_lP;uC7 zU0hJqaM6eeXD>CMEVKEc7W#O!$fPL2`_kCxj$}^3C+3Z+9wVioyp>Xmr|B~-@?PLZ zSW8+np7`#C#7v2Ou>_PV<%BZRvneh-> zZ$9Y+E?e{0I``DEN2ixm=2}vG4IFjJN*j=Lg)h2AO?lATpnmXC(9GEoQCm3jt$vdr zVyY2!gm-g$l%69EKC^o`RH^N%ggBVuv>Tb^ggiG&I8jK)h;$ji^GV1K|0RWhhz^O= zN3wwL3~oA8tmvDi=yx4sup!)-NlASdbC>kFR>d|@)ye|Of)RWasKn|^+e=Y6^7Oa$ z<;$Opt}pjx3PCB+%@?nYmVb3qhgxqGWOLB)uDP5{xYv)YuJWi?n$zB6hRhbw)H2Rz z=CWQo19OU4T{Wu;HWJ!U(mFC9{dk?KKB_00r9iblM-^L{qa^us{dIfRenBA@Ya$m% z@FeHs9Q6{R9IW04(2iNNr`)cuWZvFrr?E{Di0p`d$!o~2xF&4ZtXKJ<7Qwp2fFJ(hbp7-O~ z7pNCq-r>y|zw>z6n(=Z+Bdc`xiDf{oa<)u-nNWOJFhW*sm;nECB?n@;6U8%4VOGXT zQ{ACg7Z+xA%CYFSE0L|6A$c+2UdM%WxJ%Q~D&kq9mNmQnAkRYXXG^Wu-*jz?U;3LC z&g5>z+AGRN2>A0t)svN`mPSw$t0C88j^R~kNEm4~2=h2-yi>|MsLca%@3^deEF-6S z6rZ)01HHP(IkL5FEIZHQ*po3foJdr#p$0Sh7E~b5v0;`LZL(m`!+hlcl&IxbiZLVE z#IPBcwTo`D>fNK0Kjf`9hnIkbjQ~UObDn5mp*u*oyQMH2ZcIp)l) z)SxeK2ol#`b|xDRUx)(*7;PjK(xG~w3<Mal^Y zL(dXTl=>~7x1t6(n};bM9$Qs@5Q2Jsht<9R32Fb&UYzeJ;pHH$z;{KZ?;bOMbXYL$ z;n|v8Xj@Aj4iSr&OABjykicX6B z8)f&2t_`o#aGV;ES-h^`d#s@zzG z`4=5DXaabd1ok?5d^5TrJxdb4pcjGtuqO45YQa>8%yJ4{!wKM*c9Ub#D*Pj+R#ei0 zy1AbwSRe<*k(NERHe9c+Xl^TWTqB!2%$1!tsN*+rlSRu4W^u`ZS+8tx&cl)R2wrY` z>K(?rGvBk$MiGb2+1yRZl;vyE=+XEx=C&*tRmjr?$I1WiM(BJ%>79cz}R9bcpXMF3yZzNhD4+VaS}>p#wG7~ zS#^9J!dJC!(;qN!23nEN1;Z$&WMI&pYIH2_}ngl_|78b$l5r=b+B;}X|z0WYiF^~{#c z>(U@U&M`jbB#wwRs~T^4r=PH$F(8+dx456Dv1!5k7`e&gVOyX#H@u_cnxi|;y(rX2 zH-YYF5_4$|@5yVOw{7kXAuAUcp8Q}e`>&qde{@3vEit1X1Y4TU6Kud54zCs#A$U4m z6Hj$?U+Try`kb{}cpVl$F80~lA!G;I0Cvo$+~l;|b50E^aeFunEpO_jL@}KJN#(kV zbB5RqRKz@TI83g_>Bv*MU&O?tWwIc)Bl@_|%AC)qD1l2(r0-#vNXRvzTZoYtmfWP) zS~+F1aXsmn$^aO+xpS#XpApFxW%AVC6x|!bv9s7iK*BVyw>$eLxbrQEERF4yK#v)I znx;ic(3E^9-^x{M*8x%}u@KoNhlbSB02BB6cy*K_o%c&)EC*IdJsMW`+GQ8MD&(S% z(5v*Q z9O5#NC~rI%;N*wxnyY+n7+F2E87|E4de0`m(S))wjY*B~GCZpGbsgUuP^8MNbM}&U zaJzK1*0eWm(D&kWMG7x@Ug;FpHIwdyZ-T)?r(j-1@U@_2B9ZM}i#MUk$8eUxv?pGg zH3liI-j*UElo})!_lvJbcnJ-a=uaD|%B~fDnl3XUuc!f}nx?fOWE@i8a7mJ8N4;6b zZ&MjzmL5U-Sf2y*v94@f<4zCt%6M*^V8VZs(B)k3MvvJd76pZYxN(=A7|iLEr)Pcgz)sAgdoJj@ zN?OM+B2bxXv%?#%Pw(QV3_38KRDe)hlQk0^k91igGi8?@mG_ejz`wnw-hQilD*cK4 z1~(WsK&B0y!c7YV^vp;ZwZ1YT17IQ~oBmwQIrZ%mM9K2{U+evhCY}gRjrYkZAV#e1 zo>p6qr~x(KWxNvhPZ%#MPtlt`i!>`EJ@ebNm%mVI%*fST6jV&GEQgNN?o8o(-JKpc z8nibZ=-ZZ1vL;VjXgZm5X!%q+C$#&xt9qL10YJ{Kv9T0l;AafXh$6YTdkPgVQopP> z54exqEBb_IDCDNUTYg(iW;%Lb(blby4-7s{r=Hcbv!$N93qHqu*Et(p6=;sTcM_{~|!Y za`}=Adda`tx7Ugl*Pj`a*KayvP%%!J99e$-oCh|cL z^229}4FkOfi=c{S3uNLNYgjbHO5q0ZNiH%Cy!THLhaLm8xevmHT00VE^jrfn@LxnT z4t(RPX;RZX+!o@`ZlkCfr$a z=~#=~x;6ApvT7_NGJ1LG9{aj|R8WU*i<+#e*Wr+Qgr-DAR{{LX1na84$I7KJyt~(} z0S-Ab!#FmhU^2+Ex!!0--)FjG=H?VzWq{?j=sn@y z@x+Q9Lm9YE#s}`|4Ygt6`47FEt1VPW(0s@aDiY@OV)gDmRO>%}19)hf=;J!9d@v0Cu3AE;W3iGxiTu zChLm|G?@2V8?T0KdUfsSH}@(EpRWDrhCtN`6-~M#1nVnvZ$pp!=N4jGH>Mj_*xG(V zU*j(AE>vTani&MfW(^h-=+`=YgM+RpE-!kl+}8Fh?9GxKJq7-1;5$%0HeE?t9V(FU zC{K9t#pdX{T{+{`cP7#HrrCXIBX6h}tY91J>Kv8gt}cT3J(c6Bajg7staX5G-}?Yt z*Ha-_B4g~$2iT0bWc6Yl68y*8Llg13oaMb7FoAW;Dvdk2@eaG$P$;uuO9)kYove(z zT%du7>_M#`u|l#C$YrT6T> z;C|k&m7c0jmEgc9?br2eKsTH0uF-++)EVb)0Do9XkTYUv?^fgI0?+9IMd5XO{Zs+> z4`#JBemK`Wm`LL>Tvs@lQQ0(^_Xa8+%!v7mZM1?;GqP=U&Qe#&UC8`Ga^09ba-34m zJgmAN4Bp1<-R2(iQ~opoY1|a+5id!3@4=#8qQ?B({aK0p8*(`?ITFpN-$UlJJxC8N zW8}U1^%E7ENJ=ouyxqp*jy@;?3*P!rKyy^FRyE0qcxi+gu)gq*UO@UKLPFQ-&aLqs z{TW0FPY{~D6V3eN<8a!vkPmY5?r=2|==a*jT)S+khjf#-6*gRC+IfBW0gsCPc>n>~ zTFTn4&u(9<8rcnI7}1bP9LeEam~dv@+lQ{D6h_wz zL_<7`P=+Y-$Do7R%3?t%=~e_dpQ(HWXS)$8ImXr4_#Ulf&R-EtdB~8P-HAc%vDvfN8m0xPD4xdp?AgJ;Ja3B+QJT}y?5U4b;S=1AMW2;ArD+bi;)h& z-0U)+4o~y)6)ps+C%pOqW-pfw^0<9IVTKiQU5~*Cez#QSb$YF`{CY?)(R<4g@7b>G z_}pV=+hwL3tsYvHR`ftB%(YBgKAx|;BOGDLW7_k6C1ki6J~Q*Q=h3fws$Y$L!zX6# z-C6I~+RDZj2hI~+hZQ3VbqhScan1Y>M@h5*~@#`5itjY?9g- z$c@vVP8;lJl~3f1%?E?D0xFpDon8%no6dca^hbAQjzk^4e^oRNrFYJm?$WC_QO)X* zu0e7*F3JrD7ceJQYh)9ZB^C-NNK2RVe03@?ZE{C{!Z9qOF0`4{i+yr&I?`h3gI4SA z4lA_n>y^}mxWU^mJ$N+E7$*pgeH(Lu^;T!>u8`-c?bLyntR)cQ+yd; zFca&}oy}04oS7Nj;f7wTAE(`c)Sj5BARHD_IDPboh`Oh44ll{eUOd^0Gj->} z7pj}gLE7%*O223R0HZ^{{@NC?8RWfD32J^Y)8_D_+Rg}bZca3cq41_QY#~pNRI?$) zy{S0(V<>ZR=!j#6@3R8=kA+pXa`ejhfn8uEwv`1j{c+m6dWbAnzc@&yH=`W$tRxFU z%m4TzP{Q|pSW4hGzdm_$1K9WM6l2K@LsF+?w(Eg6C{Zr_h~79R7mqzKI2M?*hRlU$ zQ*rJcY3*$HO%H=U+7AA{Hh>S14=9I4$I_)R+17>$dcpml_~8z8y6;Hd->j7DY66i?JB&W`UbO_=-WiEJXM6dBPDYb zs1~%e6o14&L;m$+l3yU2s4>qoxXXMolhy4G7psdbzzwk{Hs5q5%)!1UQAI-_Ljjk_ z5IBu5zxzp0NiS&RH7TpELrc;g?yU|3%tsmhoE(5qAV0Ir?0sjAY%rjwonW9_N`ZTm zeVFTrV>G=~Gd3{Uxw?sW^m%Y{Vxg^&i7O{dLB~9Zcv?@Vfs*ig%pAQXdtMDlU2jkN z*=J1aeqMF)D`2nbs5Mt3@nH{vL#JV}m&^M;I*BoVfNlW}k;ojSEsPCV@V;*PE7N|pI!hoSmjFzb|cOIP@s zC|{9r=gq0|N57=05=kPF%3u}yeDu^bX%b=Q!bA(k%DY$Uh_zzV0g|*06mts2JWp!J z_S2t^4%uqwG$8d32cnbWZP{z~&ipLjGyB-fqLL!lNtdSz zYCc&&5=42>Gh0(X+|1F`BCti*K=rIxoOS7hjqLiE>+%Cq)&Q)}X8GwX$^2RiTn!v* z%g2!YHMVTioOX6%yhGR?B4WzKirEe}iFC7C1=Dnb0h#Pu;Rwx`t^&LfFu|KyCSw^7 zJ;7~{C0dB7xml=HStc3Jfxq1zha8smL<{y(KAQLLD=K*SJTJ`%meOs6s8`(vW1&Oa zHH^2OloP;vuD#}p;ralBh6Q8wBxgF zmOO?G?O98F|45)`uNo_wy9^&2f@Yk+>sMpiX^n~^HX%nVxyE1HlzLu+USJiwHQ)V= zQpu5Z8<>FZl;Cptv{#R34`ELm^n{@Oz)`X6Bg`4VR>XZc)6BtK_%>*Y2D_R+nhZH% z-U1-M!br9I^1Y2FcPbN+joPh}t!UKj_iyX+;*jc6ddW&F%{Eev#cqA-MvtcNrO80@ zkME0gcN{(^M?}fZao7U1Yf17{FUSft?kvAtHmge-p^HpT(l6I1q2CTagly1R@&Ii@ zrbftKbTqw+)|Z4ytJ%-rdRQz(oIkz3l=QPf^b2c*z^q0NA!@vx*7X0tWY*#&MF&|8 zkUrseJt4Fx1@J+IUAm03vT|Q)f%++XcH4e%EbrA4JlRSMSVy`a>b_PHhc0r{uP<|c zZ$vK!0D|IeLrfnbe0t-HX|z6Tr3#NTXl97N?;O9~p3EOd!kJ}!V}E0k?r0qya_V() zn!u>v9lTo$R?)L8ye1PROA`ygeXNDoP8oEFkdpFf9esA6Is6yCA{AW}Iq=hQd1 zX_z!>u0kMO_qBAVjq^%BuNF^SA7f@XKSb7uJL{8{Rv8DMHb`0?K$3G~Mdx=#-ixV9G)(M4;@S|*lDKCDX4FLf9sP1e^a zk8|y2MYq%37gPDX#>Rq^Y27p~2JY<;sVBrOquJ7h+FK)OWuM0blacDMbejVrl-SPYsDx_JTIR zUCy$-JKMK23RensZ$~meMb3iypg$SYd}CNb3^fZ6vQyS;*0m+NFQ{ip44e4Isttg? z219x`PbTX#t8~O~_mcLzH1}Q;UVEgO#gf28WNtjP8ZP2TC$~u5jkP>k!4yyVDC>#n z5gwyzSyrJwbjlrGqG=33`}2)v%>;=;mz&>HZD zH}Ve(oP_j-@JohHb!08Tj!(Jy-HMoOR@h*vx~9TU9e8C+==GilX=ukmhkSuCpu z+`>0_E3>25gPJ0p;@fkKfMKz&DvnoK7h2kE&7`Z<>OnxmvRVTk_*y=e%G$$wlgGHJ zqU&Ro*c$@v$(j?zxt34tKooP>X4T6Pumvd3LL=V=ld`(7H>~1;K=sOw9a#Uu)KnrG zDgsBJ&j~Z=+Tbqd>%)9=9S0!8ZLjxxqXFev7BY{w#K-GLxG#o;I~W1DPQ+UhhOaS` zCc^(2==_X(wq%=kd^XWqf5d~JLI8uAvbRbX$9$lw@fd*B(oKfq+BkBik6;U?UBP}2 zXw3r8TA*Jq`QCpM_P9PBBMczx>;vwDdM2VO(4;nVKsK6bwiFF`58UvkH$;k)cm?(P zOLPw7%>ruMssJm9gv)j4qH7!h05$l?e}rLM)(#ajkxqf04GAfu+H>~W*RpHI!wGT{ zE(2xE8|oJ+nLnE5_jh_(aBzSqCDAP_&ib{li(0=P^fO=J;%|&Vn@Q~(g(A)p2vfA`- z&{$Y;Fr?O4YpuZ~desV_GcZ0+E>dVmqKY!cGm9oF#80056jQHR%e0n~-nw&dx;QNO z+|~5C88yY$@OPPmjcV73i(EbUG4XIBnnn-zm|e|_#k+3Z;G28zEue2d>PkM(;+!qg z%KbX$eK9|9Z^2~>?Pr_5-HTz=(;dDsQ)j47?M=X-q3yqu8GkeaglJ+WZ1xU>cUnd6 zC#C~U+iZF3XQ#IHB*j)`?iD%qRODmR8vjd(Do8G=)Q+ra1uQw2*Ql(>UpY(>PQ}K7 zbfyQC5v0zDszh1P3YoKCRX&M_pf|`RdpPtX;AYFjD=xgiJ}x?mn+|jXvblhe)KH$g}Xf_&8zS|$gWgQq72jZG}L}I zsH@({@{c<}asG#L^=!TK?i#TEsA|;DdkXVcc`qhK(qv$d5}WB8OP>78SYBc}(W^-x+9i_BlUMvlbD$yuJ@{!V2ayK*m9 z{Vm<}0f*36@HN8mS~mk|pcAt~MbQp=SBmL9ao)N0Yq(V8xH=u}RxSMi6pY3rS*XLB znAFdpzY9Sq`AOcFUW1uu6z=-hl|He7q-ejDPz4CId)gzIztJmv;K!B7rLi5K(+ z=Rb{V4j3h9=oI4U&JsRUS>l?7nM$Zve+*=~1(`4pV@U_9+sTtc8fjAw?`m&DIOaY4 z5~9~YVqoIwQ0b(Ui^0A$60@J$rQ7 zr9rf)r#*`~k2P%@-U*&osHGdOo1;!0o6WX{6LVGQG(*~Zkk~xsBwb!&^myN2Z6>*U zoy~=8mg}Y-2vX|Zm_$JJr8*jc@&eFf&LDUmGGXhFFDgViyxVx?!FKUcp<%9%lYfSfD9+9tNgxxy2?)h zEZ3&ExIEw3gKE3w6%>Cdk29~1Owqo|ZI;58)X$(}n`(Fr!z|~z=c(9E(;c!X%kSF6 zn1ceF^6DvB7Prce!c8TjK+B^l%C<)TDf^zctt!YqF9Xm81fd zj^U!v;RC(re}KIh7s&0BY@Y7gTUn%UeRMzBa8`9a+tE7))ug25%ZZat;u5W9_}UB4 zMpxa77f&Rz}#7RVr&u+Aw}e;Q;E9m#|0K$fS~t z2YU*MeYYiz3-Xmu4Bcb}kYnElYLC{*9yUS&xlZ)j&BStNKDQ*gb(Al)!*T~t^D0#tv_g@7dvK6UmYX=jhX0px10#Z; z*Ilk!!_>*_)cbxWW3%VwH>j`bF+Sj((WEhcPjy?d-!sl5AB~l(5D&+;-^emWl&p@DXHM##^`#xR#@UYy{cs*BWLWSOA^Mz7 zDX}wQO`faGNWj*z0>b2(#PcmoqRSYGb55ONA%A_scPo5cp65wj4Ma}qln$2T5M5S^ zPiAr;#&L3{*Ye_>AFX=89ZlO&OG# zmCs{i`u@oFiySvnsEg07>S{u8_%T)LEhdXo3-<(ZX_eUt6RAF%qB{-SEVo4;0_5cOSrmyBC0ng2Lp)2$5`J$wd-<|rca5+!#F*#PNMlF{gpN2K61jsJr>cZhMPm?T9pPu zGtwn4^nCCO&6!`;%>o&7HsnrR5m->^E~PF;lc&YtoiXE>Ylt|9GGTEhP;YJ4e`xg^&^&7zn$m`O%x}Zcw5q)* z;E@{EPcB}Vr9XK5F<}DQ;|Zy?$pydY(pHPo~F8%40w!I(nl*PEf~wG~QflkDyT#Zo~?cM1jc7Una#c9`Ct@tE5M z${~h!Wq!+PG4lFcx6P6!wpd5+Im3-f4P*1I)M=N^LRao;V*K%kdKj>4l$%vB-0Uo48 zWK@?Z2;eD4V;>fZ3Vv$GUsUE{)>w>cnkJ6$5%Ffo>XkQNO^`>7V-Vhc?XuG*9Xi%q z6(BOx&50;4M!aYu<#m!=c<0OFB5feWViDN~#;FKm+4PAyhgxwGv&r#m`&fDh`HLDo z|Bu&+^pe0bwfYgZ)GCr8qN`~0b{liK&!r14sN76&#!N@z0XNg)Oc{$_34IK7B=eR+RFR_IXoC$$np{l2_Zzd0-oU^nTk|G$l=p#* ziBMTSm776xM2TgqE)k*{jOnh%nnzV!rQ7JK=?hIzkeU5M;O#|cbl)@mIl;ge)J|kR zS|1G(1+7L7KVukpg9!geivK@;BW^x*(b}7#pb^99C^B@c&6>h^`!&mdNdb(ERooagC*35>tYfs(M z2^07U_@qd~+m32$o+|i^7pMsLKU3gh_5@f)so)+%#k>zfI0(_>DU>tyL`+A;sF-$U z-$JXS7t?VqT4EreTe_}(Ibt-XBO|M0C{kG98BaYDibZK~3`gU<{!344*43Udnp7!} zBHmL_RbzRwKMnl@PaWy`w1#OnO30e|JMThV63F=ZVouoXn!Eehye>;WP{y4)+P4@F z4-W>q?CZbmS7jMPc;p^?d|YMYnPe&f8oS!$$?$XE1)!1-e|(iN-bIp?FH^bZsXutN^LH23BN|#z32E~DK5xXV=l!Zf z_Pr2vn=QBXxyRd6-7TObjQ?J_r~Sa;6`N8qc~VT%d~=@Wn|>|}FUvY**y^G8(|hyf@a^xn0yh4J}k+Pe)ph|coK!N+g!?QBIY!I zT_JbD3pk$9<3CklcF>-jPLJb=vSTyz5o6i zk0CdE9UEYdDVzKrUU@!Acxs(K9bq669N?KcG6*@d98B;|*>vS%Wa!8ivL-5gs?ck3 z5KHtX>+y%Dh^UrGFTKSrLTac}y`qq%cmF=mH|EgjY)B;SnlFBq zyD&Q6zOW8sol+dc`Ww~qe~t*>OF;Vowy(x+ABT>~(Jsh;NM*3O{($J}s#^MYflv0p z4u9g9WVXgORyMKvH=z=ec!jJvxeO=`WC79jGn{ynw)u2Bs+}fF)G`ofjeEGqf>CQyViK4)(aZEzbqOKEYJ$ zs%YPR(1$vR&A%6zu8VGBQ+ygRixWQz04pSmz;=v)!i7Q`!<9SiBCWc{J9pLicQ4IYJioa(u-g(;{(I3QR=i`~q4) z{~qA{FhMS(Q-aihN2~q2q+Cy7;gUq12@C_xP`k}yopRSfen+eYMxhY@zO%S~T7~Pr z(e>4JZ*>-}eU>hV^<&(hKXkxdq6|S5KFe*?mx?b^zlvx3i3G8pzID!%(Y!AVcK^%N z>t{N+kUk&og)lmvcF5m%(1LwvQ;$Ue)H7llqwF7?CvsmD^OjkbLFH#Bfpoi{XR53z zN}6VyZ-Avgtg~O}h&%azfjT39<%^mst*6CuY=1u(MEL9+<&f9VS1OYpSTif_zW-GNpAFSjX@9P!Qj7;L^#51$<$uPWl96yiV0g(yB6kY?H9Fug9_CMi=6~ao1(Nm>{Oeu*AA|fK zKY~O3cr6{78|^RrkI(*jB7ga=r3PGqCLMx*w_DFAK|g@U1f>f@`Q&5`}N@l(kdl3MUfXS_eBt4xZnQ&O^OCW`nr{r0oq{kXz0>;OWcvI)w>* z=m12WI%U|>A^2prp1OP$l+p6EYDkr{%ux%pO8D8#r{X-$cajSvQ@xH+$BUIBY)n29 zvDt`MSx#n)$@4|`q;e(+wJ=nh3D-8GPpZNSmBp%7ZRS1eBbZ3MxxFK{@M z^{(yCO)Ceh>p??aIf1RW5NmuYw{yLM4b&aNYLB>l@o{qzr;Y(p;lXAV#iMA%x?b+S zbRm0EZj(`cYps8Ho>5?hz#gvga}D`#U|45aG70mvCk$FWz7n$o9n-wvXUTvdnNPoq{&ToR zK0*m)UCD+!2!= zrTB8(KV3hHduIxqwP>BKG(6UZzZJLtf=X0c$-Lz#$BxPQSyCT*$TA8zNBIVsIa!F6 z<)+=jYC7(eI`&uVrnU~Y!r&g|kejU+O~T$Ov=DJSasKguVSi}2J??#@b^fuqf^q*h zo3j1-ux(?Pior7`mXM*6*TSSYr8|_peuBc}kA%{XacLC^QzAajT?z}YX)x1(hx_0u z_WeAc>Wb1o=LtU%{hM>wJh5KnykGs17-iD_3uUsF77;%Deu zA>BazWwY544R%qpdDc)&C#7#IKh~2R2kdd*eB-TqipQKth)zwP)r*n-GLkC^Lap}C z3F$Kog4Ara9HrjrvTnSJQPjO^m;Aqlh(&%BTk+L(QDGlqD8@n+dHiF z{zOqqX{wl@YS1(*Otnxun--|Qq(3}Byz@U!Ge7T7;!2rScr__Y-&_o)h?pxL;uX07 z2&E$FV30>>EHf1?7Cz(UdwxX)GZ< zONftv;9G(ASi|bGx3k2Gno+8MShTRms(a`KgFfF9z$Ga?{^H&h!@&5H43myNP9UPV z@jL#xpCo1MYt=3M=@J^G<_8sN%@sRf-ff79XBsF&!XcvlNtwAj$s1*c5lraDu58*q6k`0?0VNy6)?^ zmLr^1`Cn=lJif4Dg)7?6yPI_cJSDhZYR6JZsW9%A5kbPrpert&Bsg70?!LFF-xlAQ zD)Xo78*q`gDdU^94?X1*F5;3~0l_NkGD1)(Y91RWiNu4 zRLB4my04osdCJv=Tig5NSASdOaNx=(mI7q#4!&KEFDY{K7)sglN`!m#VdE7j);)9H z8T*EYM=y!5>-O0Dq@SbG{iOGFEs7<2{ZZT@WMez<$B!S+)z_W8OQFvsg#!XP=kXX4 z6oQ@+I6TAiyLwj<8A+&sf`)tG*~uCYLH2$5%Z&0VQTeM^gMKZ92mmoDx0&Kue4T6` z&GWZMWCYLGezlXhO_*^B{X3m|y@jTt|!(K@QC8IP&N@;F1bOYY|qukUV&Cr8_g5qOFW;mPD@E6jZ7t3d! zG6=?)5a$mjIJbKwuF9uBOe`c)^ra^Sg~mMV@yOv{r=Z+Py;^$*&x%OnM~)qfA~;H3 z>465K6TwbV!OM#6Y;||$>L}(R zH`dC{5RvQiSz8OxB#PB`AxSW_0^s;W)KS``0DLL^%iH?T%!toCUx==VslU;#eknQD z3=^r=C1*uoF(nMSKHs&Sg8KJEC7U-G4hAJVV#^UewSd-iDo~pCb6hME?iYe#&jP_T zoy7+CLF^Lby0V?iY5B595cY3Y&^3V%Ep!Jca58lHJOh}txiKWI1m1bmnhd7Ma)py| z_@<$aFo>;AzSso9KqE-l&OK0KC|q%PVxs9eCS5PcH(^WApX8hn3{ILi^Te6L>$uc4 zn!aoph1mIAnGDfSu!Fc?;?JjZja2ky6Oq2#7ch8NW{whg)pSh_d4r*D-$ zoSDBxp3!3U?t8eGyKmCZIBYlp{V>+A7o>R{PVP8OTIAJpIsKhnqXal0Nylqu+T->f zMHl8bw#g|?*EBU>;ng^-=fcVUoRck~^S$Bl#V#+m&fNyz=WC(5r}C8nOl80NY<+gw z8t~~eI^HJva5xj~*FJsAR{}Hijhf^ZA-p}U&~1L)yp8mf)c%sPq_!&q1VXXMo*(g;iRNl?M#JX^b8B}AKT8}|Wp9ni5nfHdyjxqHxAcD&RoLixp$LdY z%pbP1-+(bL_Q(_pcmr8uFG#r@0yrcGvx#jRzS-1WnO0}cM8AdZl`97ZH_Ij0(_S3R zlfCA_pwj2xnUrUTAJ2MkWRlQHG)(4mD+ExYIl$F0)Wmo`s-mXzUM%!^da+JJ>H@ID z#D;~;`s+Z|qn@}Ae5XXOvYHJN7hqSlFq$q~<)hVW(*8_#(!Mi3hy#IMcuDz`C9B7I z6#V|aFb{RjEYzq-Xe7GO*N>%lSG8bj%df(rzR&nLU93Xjx<3TgaFr$6rTomPjPB8pJy?eV?UeP=-xET_EA^vP)w{|&fQJ3 zu{MuV*^1b9H(`acqCD@raEJ9C3t3gZ2x0->>c+<8;*}#v>uudj$Ol`H!^=(PaUG_H zx{iWnTxil{Q0`B&(qPh^{4rolsY?zeg8C@3|A$9FZ8*Yd=pEYvjHH{Q_TYGTQF^o?iDw)})JaF-Hf=nd&p6O@e*b z;3#B1kJsqsJ}3P(v^}m1?y*3#T5{lvY&TtMK+&JflX}nREdB_~xyrO(bG<~oe>?VOz8l!qEag{9h~hREjh^oV9Jr!~#mm?tbCO7XSnn>YxLstd*=+rb45ZB^+7wO;C>zmm)brXcFqOcMh^mR^@G;ojDc+)7pYx?cq`K<%7nIkiXqD=nB%8w_ zcg_d+3#<2pM!H{LOezH)ujl(bG|i=yFiDU(z-q$akxnx~C)D^HSs%Y@Tc2S5yHl2p zsPYf@URQZ^C#K^C^CaI%N&R-zZa4VmYE5G$bRxmFso=iGW#4m*vohf3&&A8o;XS{L zaPQ=O?_MjPFP*H>&nc5L^R%-XcsK0zu4zxyJmVOBbv6v?q1&Z7YhgXJR1=$_`;dA zm;0bitW3X9n)k{5jn)jJf{oB=#U+yNntNYYUvAwUQsm_gnO93r2L93QAs?Fnyif2w zhUUXerOo=~-Bz0@4u1lB;MoXR$_tF^54CZ<3Ukt$qGb~~MjZzk%u=o>&WfJXYJa}j za~S(_Rf#*7TB2PyLjdnIBR2`SSJ+LXUl{A+`oUral)U;ga3|^MY5-yUPvs0%tU39J@~f?VSVN}U#FbYDcap_U@XcxFT|@oC|xgE>j4r0Bkh3=4taN#`R$^sYl#b zlACXvikmbQvvMbHP|k+1@C~=LZ|;19ECL2iIQu(rc!h)BA~u#oY5WFJy%r1z_Fk>d z%y%J#ye@mj{5L#3j_nvuC;gN^LIfO%TrPx8#axaoYs@cQPh9yu2V`cNta*H?@7Gr4 z^FVut+i?EeOcgao>vaIRU8(oh4%Dl6dbdA&f!LqG7cBd2FCEr&qwD=J#2j`NrkmQ5 z@aVGjV!R9mh7iW*;+Bdf+ho)mv+WY1=5l&#L1woqauV4iH9!Gx4wVoldu~ z3?`|pDM~q;26sic?|`x;=tYcEF`M-3qiceqQf!&6C|wP9nl4n`6N(>fqd59JTEoISM9d>Y^>N@7D1OA0bGv zq~W-rsAY&|;Ug)l+ZzUx$A~PxtsPkCGb|dvf7M@|1G=qJW~c19a0J)fQ#}1v7+cH} z*sm{qU|Qm6WKWLNz&2rYb5+ea|g~ z-P5}93Zec(GXNV7m4wsDk@DW`Intw?LN)W)bPqAu@9O!d!a2II;*+CWkOJ5vOY9qJ z(X^AzxvV~UeC7ChZ(u+xi*4S9^~D%;3|0K1eN9r_e2gGmL+yP%c)PGo3RgJf5lbib z88`Ql6YmC7s`q)FhiJSIs0e3%qmcM{gF&qOK^T9h^%M&|$F{r$4TU(srn}cQm|5#2 zyuq?&)rSh*Twg>Tx_v7si7fD8*ifvpq8=_nwP>cGpYRS&SYT@D7c5#$PlXzz4-U_q zhZ}?b(g6M@;`!sJzkJ{4QdMTAw+98+3QZk27USQR##cIYFl#MykOZHFB39})=zoQ@ z$q3l%osND@-PXA$O=Pt(hW@raFM-V)qCZG##gg2mkr<@Q+;9yLXO>Z+>{ppXd?ZQ8 z`~By-R%(LA`{WYY;|tV&;(f=hyn<<5vgq-98gRNTfTgQ|9$(uIBB>6A9Wl*LPOML) zs3d2FJ#D^ExPBfM%P-fp?<96ppNe=j#F4mNF?jI|Ld?-5C=&EUs){BEas<*HEPipb zTrVE(E@XU6+?eM@N55kC_15G@X*EOOFmz`S$O^{gpK}{rEs#( zuSCmaZr5y(lzN7j)AwAo`I+cKdzB89cZXU3=-I7?q9SRV_}vMOU;Q=Tf&q>lKDw4s z=8P1!;@)s3{_lhWk3$4eoQSY9UAPI*gfb-1xlv zE>0FvMsTHdy=OSf=11)ob$QgSgkblw5qoYhdFSPSZ!h_VOW)Y^lcN1dolQ5%`}^cO$JTmoqJtu(gNw&r#^H>H-kA&A$XZrk%>t(PV zIFu^YcS<4dCiAhvL)+Q-98Wu0W&bpNX{u^mX?fBqjv|af%E}ZSIkv%|6?WXz=Qsh# zNfGrnrXd6QxKW*Icq)SAAy;bA$uh|5w6*wx; z4PLRWa07PC8KFU-#ee6-I=HS1B>!|ogIx_tIndcGKPx39*}dHy_{h)+B`__qKG%$^CkVSA&WG3Z5{ z%(;-)5R>zzSsnXBbsS9kRl8aoHp{6`hv=c?eL49C z?L&8o0r&*vXphpwnPJO%)nEaE{_n`v6K%+deUr3f3x{{zoq35wV z?;5S6j6%3{%15w7eEC}a140gVf}WivT!4~AmF0Zq@zH`OjK<)5tlMLyD0h1)D3=b_ zp(zat_2e9OfEPIex)6=bt9=w0IX!}`RMkO#zFfbE6{1{oLt7s`!gF!n<2SO&%v`VC z79sFIN{6_-!k*52Ej^6$mw)wfrJO;lTH9K8a3gAMNYL*%aXY?4`bR0YoWC%-G)S9{ zTa-lz+{cl+J7vv0@1$wh()ARxpl1&5;KQ4IN&n*qb%f^YHq>#iLQ3QreY*nUT!hxc z8Pq4sqHuUyf}9v7sEOMc#B2&P(Z^JjWijzFh27Z&hR07Q4F^~Pf`Tx}T7^xsNdg5^ zOy^n%IBdoudrtD7WL24*^cFNsIc@jh1Q%)Pp?VStZPLup$qWI7%XhT%+Y_rFF%-P? zdezFPUMB0KYUJh+aLuU=9C1zjWANxKkciK|j6T@>-gS-r<+kfpV?C+6j!vy99!607 zef+BFV!=auv4*BKrc*W{-<1b;Z`G;`xsO84un`J^A_&rFBvA2MEIX*hXqEF? ztS=r}Pt__|yy0@$Bbao>*Bm_JvCx)$-%~3%ggsOA)^xUKf2KxdCLm1~xy=t-w4Eo8 zQk2J4iP5HvjTu{lV=Ix{c`pNRc{swzFdXMHSH1l?f>DZ2y$l17)mM{y-nV_O5n;ZSvTlmsqP!y<84dosj* z=OH^bgErA|7$zZe*xNPPaM0FZ5~V4DgIg~7T0XfmAm?pm$!4maNdt$~-n$pF9AFP8 zut<7+K*|?|FF%CLYPej>1FQ9Cqajgt#(c?}loHVSDV%sRA4WS<{|>kJ#JaYrRsQNO z?59g$NX|mz`Qn6AyMfV})x{IvZ??@knR1Ks_GV&W{S-W6RC2E%ObjunaGlf8NG5<^ z94Qm=%#OzL>sit&^$qJk2q|11F64S*fv6&m(y6cQ`vLnnGMnHTG! zlvo+OOd>~c8TwTb99TS*sujrCXV5;6+yUkLCH!HjXx6@oR;@rZ7?W(J%#)(=m*$Vs zEpC@RPR%A>ou7@p66KfiURC_ynsFW_krGpY9%Sx_n)XYm|fsRJl^_WWoS_3 z;M2F8cD75SRHc&$Y3W=!tiQQ&p4N!c@GX6(lCK@lwl|=HOp^YsLz@a=Y`x>obm2`I z+7s_S;UY}};=81Hzm{JX5F5EJ4=f?Jj|B!^$fT=X;Z@@YA2D)M@4kjdk){?hiFx%W zuqz}~Nk!AXhc!V(!nc>=Uq(IN#=@MnOf8RpKAk5h6bsQ&n*Jvg3`aZnR8{R(FW z8PxYAZ5{cn%D=)xZeEet)>^UNLb*uIKXpDXnzboO!!(YsazGwdjhq1dpnR!ysh%gO zl|Dv}_*RN>n}+?_HuE>?t7Gpbo@hrRMtdGz_sCje9`?|cyjd1m`pQ-it2?*lkz)Ne z^K8LZ>v(i9dp&6qrkPFFMEBVt!`frLwegq&;!eYOqGfgSWb$tdR*{uP@25@j($X*Z zU@KK6kZVtPOUat&_o@h-y_l2-_^NvP^Zm3dA%;6(;(U{=-3mL`1z=e%73&^TlBOm_x-DD)Xi2|-L}9k9uE6j z2Km}b>|nJ}Tscacx7jnl(^^FuO#9hiFfz^5OE@?w;eT6+yzpz8{@j9pgO$=5fln~T z;cd0;kgE~OgZsQ8-$&Eu0vK7)+y^!3l-0wC=`Wuh%LyzIcX-~Crt*sM{G>~bV_GN+ z36FGjTk0Pe1x0MO+mG|t9<%~3n1$2rDD7V;Ty#Iq7c8jA20||uu3y4}F-dHGb-~Hy z#;0SrN0fb$Z>1HuI>zVjC$8@FR6X7qJW zQ)A5&e))-nCHy7quq787UcVd;i@ez2tueQMuz(%~uzk&|eJX(dD6Q8^92hHdYvN7t^N5cc1(f9~qoNgm#wH0_2 z?IZ?^9=q17$ycUr0q=YC6c^xfLBYfEgw9z2^jM)F<>yP@X-tOS(Vd(Io?s<+A-$l} zrC{pP`~?Yz&jAeAJjwjmLOd%<;z4Bk=2Rut<1d1*7Lq2E2*#{bSoxjno=+NEu znr`=ZNYDcUYZJ*XoidI`v);2*IR37&xg%Xxh#-;U%}nKg`;{kMGfu3hIlmo_T~`K48w zB%S@wKb4x3$z?TLU$R7evAIDiS-)T@=8hJFf(zJuSEKdO39?rVkBp(<3_n-og?pRH z7fa`pxSXqbmhkcnUSLU!N%a-xA-O49dDxQAUMCXELo3{)jWzExI-x&_J-n~@bx=(V zc+X0EI^RXw4{4>xM(YY!thGl6zN*%8v;7e_pfvUCVe7ZT#1=`a|H}N3S^1Dg0SLuE zvv(w{g)pV2vfdXU=ZfjV9iwq%8mQhVJB9k`iEi<=LkYsdsvDN!1@<``Wh2Z-;M#H{sTBJ64Uoh62xgblSlgQBS$>lA%Cmf(@xJ zb+~yIS!U?G6+gkEGm2##s+ipqu(Xbkb5e7_DzGV{O>4at&OxS9{ekop!>&D|>P1|I z)f~?mBBV<^t7MWc$?cklPDQ~TdKIH64ml0qC>k1=3JV4)xj3>N80k8T&jsZA6@~8` z+f&yq)-HCR&6bV>t+QEGbod}T@M=?Rcq9{h|WoA zflaf4-RixW)Ws?(0k7CehXi+0M&8=Gh~u`1%k}KrKHGkG*K5<;iTsosq`Knl*~GIQ z?G+6+Y>mbC)Z~1Y3iekdvD#5jKg}!wvK<{;eWXX1g_9}V9|=md6TKJcuXDR} z!Tgi7{rw`l_3TN=H?4fh;b#aqn5HY;7)4smzHU)|@)`5OfoS+IQ^K%WnqE?i^cmP; z^`mf;Oj!gYJH=%Tvhf(Xdj#dtv>8H{M`;<>d0Y0Y-I=`j2dK+YO~&43r!^ zavci^&MQB!wmuA$>WIXe+SFPjed7I&e2Bj|n*70weLrHGvR@WEW7|kE^$>jz(N*+r zGuaaBdCS_>wV&~QZYyIf3m3Zrg3Ey(>tb)Sw-GEc+n>%1fhx>ail;gc!D<-$M7jvI zGk0JhPT1+oE1EwtwB@Hm1z+v`2+v)M5Kv(t1^CA7bUJbaGZc?rJ1fIaJj}&9YLux9 zzyR<^(}dvrr-Qo9 zw5-ccxgF|7h73(iui*{Zl0k$XBJ{aoQLRti`rvhbA&nsU7U$3-@E7&EP)rZ}6_px1 zYa9hWc2I4Z<^DashxFnbKkNsD&+_{D3?)GO42C9E3*Dg0W7&?>e(Z28zP5l}S?=IR zsJ=kYri-l6dA>h8{T0JinfFA8bi z8p(FV08OR}DfeAOc=g?el6xg5LI*UQn5R=UGrO*Y&*@Soxx6Kgc{phAkrGc+1LW=| zk2SS0jE!sDq9Ke}cY|=5$)E699O|R9-N~-=AC|daNw&3Z%!V*c*!5nB3hb^EtM^;E z4DloFES4}PgA(D+!fgqN+z|Rb$jw(<-7>mu-ALrI)fg8;C+cVGRp@46PjcYW-03w( z90^Y^n7C}H%;VZLU**XqvpB}PwH}*p7Ng_mIZ5C`s)h(RjBWVa#(DFU)$@sTkYKJlDNf-{n^F1Sdmq{$|`XYFo&Lg%M|A? zxRmRo-b0yz6B;To)I~<}DKos1I>Ssrl8&a&Kj?ozD^vBGC_3QaA^cN@5I25TOUSeF zZq}A9uiB5k{bM6ZiC*S4A^RYYisExR8Y{j;MzQcs%{6v z&@z%BZgz~flGqK(*eA1gf9eI}XV2E^V@6#G5b!2y@A{hi?cE7u`I(Reh#7WRdz7QQ$n=F26|1a9Q>$puG39S5td|I<6-?M%f2HP6I`FIG)nS zU%F#)I-XHm2S2|Oz4J}mP+0BFMG60S*F$_ZLvm(8#gbBT*e2TO38Ubr%m(iW?3<1{ z6P1Hi3NniKP^e@Mpe5WoGpq%q6f3Y5O*s1d*G# z^NKHn0~5WjzHTwL}b zT}5ks#Z&)D_DH!zSEsQYt?g61MCz0JN#n0i2C->O$Ug|HsH+b&SOvpM_NIdT};$&sfOHv)G=6COFnVf!Ahpr_4LRX=9{L+DUW$peJG z*4kwjE!5W0I5O*83P;bn*98D;u_)H6LdR+sQz8xlzH9=Wh|99F>}l4ED?N71FXs`d z$t~Z{&6?V>P-n;?>OvT2K4LFL6QD=jBU2YfCA67ke>m6fI-F?ylmW)USoUWt6)jy(l-fyIEnMp#ZLRz-~jIu`$R@sA^Oc-9rSLZ!23PX?b>&3?;X9Gr!Y_ZK z8uz?J4X|xI%O)pg0O>M)w4SrgWL|8ki`C)GZqa$zweD6LX3fkwQ&T6|uWKZ(ro#Rq zrw$N}Z0yex%a`-QWr@U4cb}b*pGp9EOIzTP9$ zoB>*!BGd(}@OutTpRB_N_ZzIjDaR{77!JF+@kt^2i~BAlQ2yb8w`&&Y8!T$Kn(B`U za>3pe>WX2*w*2Ebllc=qxAujOsagT}f^j0WAvk)9B^rvIki;2#j{H?xwbDe$kGTUee*)2A(hP!m3n9wwJgnKhEcXWFn_m-oLm7|lsdmxtx z?bm>-8+`mL0xe0b-l43^M5D-3%qGhY!s*fcj^~zgcX_xzk8WNx#LMGcxroDJENOz6$n6QLT77&Txp&G5qz5)Rc=D3;TMYS00Zq)I@HOT+FD32iEe88Sz&k5muI<`J+n5APWv@!zJE z5YtEK1v2quO6EFmAB@+~zQ(L=_eG$w%{5yow47k_`+);DXm_Jh^dnF!x`#rN z%U1$-AYL6=Zf+`qj(>~BN+vih;q+b0-Mm^izk&P{mhDf2PK;|4YmZ&;{s1)Qg3d^3 zqW!tC#!depI!NtJRtv2<=CAIOFn;SFVGIwM^~w{;n}ngddXiZa7|!8KB;(gVac=3} zvvKbN+IE7ck)V9`b6=aa&<&~hK}+opgRhY@o=l}%&7x(huB%OkI`R;K1`^?{1*E+h zW@g-wTWd@Q!a)bd6%#2@!^~|0X&N<4FGu4F`%ga@#0R>3ZcTTFnz^vHMEGF3P*vgE zuk|m(1vQ%l(MX&s4ZAN&zfD_XS*lwJ&Dqs2>sxi|R*>?a2no)kPfUA!Vd!x#s}q$wBuLJ*1~ zzbPSaYF2IRM!Iv@EdBzNgt;oeL>W11cz9Be0vv;cJY0!*|9Ioe&{YyHLQ5AWnyhp! z0ZOh6iEY^zUNm8IZ=8W}GU@auKy&#?T%MeO;JXV#7(5VEU7|?C4eWbVxCohzVdj26 z6olHqp^0iNj@5XxLiavp`oK!INQ#!c=@%MQh_3iTzaRppB0vKzJEcGI(wpKuOJO4+ z)#Kr$6KkF7&X*jeL*X;Q%VEc)LVFyDTK?`2LK-Dv^)HDGEN2MJLP;q3;%Q!4wci|0PUuqJT#$@R%RK@gDUz!-D&A*>mZ>ruJUmPLF#0@%;Tji*Cu?l~U zWNic;BH-|Pf_DvHK)+FKe%$5|CKn z<7Y{&D_Y4d`@s@!Q|FxV!D-BUkepxhuPNVm*H45)ursJ=%cK zprvU-Fy6vMyI1QUbbF@B&qx&>oiH~mLnwa_CiJ;T>6nFy--H{9R&FgFoVjmEvq8#d zun!(`>b0{%iR8~;(LDSRMr8;1Qca!x$*WK6m18a+Tra924^bsc9M};hy|!PM-S=i~ zoe@HRG>pTZIo0)WaC8rs@At4k0X3RzCHbwVTgfuNknZ6eWN9FZ41~Wbr_>^6~uO)Dx4DvXLsGW6;0}a zb+#Wk(O7!ZZ^>csSeHwF_opV|9I5v2l1nD!yQK293B>Uqi!O%kdeJHK5@_wv?B2bG ze&(%M&Z_aF=r-do%PgZ^B;OAS2${sXGpZ2T@wb*+{YxwG!i|hR^|$@q#G47`;Zbqc zy?nYUc#S{sJ>#??w&OlfWu@#q;VL5E< zfD&OjWGvD?*AN?#U4ON}dI1^^FK%qox7g74yYJ>Z5l#&)4R59~(e zP;`j#q5tsn(9i$`v3Y)B1Dk{D17pF1T{&e29j3zc*c4ENd!;i3BfB zR!KX2oJi2!&kP>W*hRy0B^3CYz!&7AK23MA;;R!o7C5H|P{?OF7St8r46+1LJ7_63 z`AvG}K6c#&UujhuN>DA8BL)EG8BTw=o?SCPovP|2#m~ShM=fRO@St*>QYZB(Q0vYN zg$N6v4J!jp&SWlYC0P52PE*Y8w0vrllGE0u>oxkQ+ujdWO^caU9NRKd<&HswyX$Oz z%{IPI!HGyWL|q-PZ|ZnUtrGdj^&fqJf?^VoU{?hU9D?j$0AZmj)L8in$zu4d zqlwF3ZQMk!zRgwBc6+@C`7pB79NiG69l6X5V37ho3D^fut3w(>AK zb2mvkOi!!RZVM~dJkQ;Jw5|U@z}+#-e!w#I$bff7=JB;5H6F7*L^HW0cA_zTIF7Li zd%2Aw)-I>l_J;L5*RwP56Fc;{=3|J4`*WXL>_BWRATyL_jrVRFi(~9JiTV~iSM*5r zyK>3_(S8uK(Zy|nYfrr8V{%jgFPj|aY|P{XA}TKIm3OAoTn!^Q>2qleX@bbT0JPiS zC4$!RU@Uzxx|Y^C-qGCg>@QB+p=ojkKZku$U!(#~>lB$8KLlXQvwg@Zpyc-E4e~@I z^Tg(vt6W6g8fW>qAK;Sk+1oZ0+(t96r_C$NZb$L-T>(mj5wpRZ$Awc$(G#W06d00X z{(G;neJw)eeNH?LvsO%V7fml96YtpmtgO(=h*2j+e-Qs-pk19$G+=_)+5PpHp5+4W`>gXpC#@C#P zQvJ2*`k?YWYcb{EB|lPy%j#<>4la?*mox7011X^{O2&@8f_`E89Y{18wY*FXy_E8O z@kAZwr2LIZ_%fW2&J3C&qQYc6C$WwG)NifE<`waxJ+|!<=KXQGMOUmOUUX1=7ByeC z;x@`07Hek&+>Bf5J%V^uSriv=I|~#d*{Z8jI6_gdqULYt?Vtp3=W!1aY9JbXv}6*U zdSQ-jcH>i<1%>Ds-S&s~)p3}}_ScK{gPJ!c1$zrL3W+im$Efh)ufk|~a9;(W85l~8 zz2z`H5~u}(CzY8iS&+f-Y+FIK@?Kb|7lx}9(!Y~uUu{GiOh@dojO#{J^-yBoxe8(h zafN3*!g_f`N#5riXNrDg=2Krq+%yBRL`9tylpvli75;L67z9E-u=o_x1VC`x8p=-( zw;Fx<#n>FaYUO~*@qM)OWV&;dOKW3U4Stz+y{>i98j&U-d2^8Af!;cX3J{kCXHAwy=kuP#aJrXjbJ%DDw52g5#?ccJ zLu4&9Vg9B14&!WYv_7!XXf;cMj=xk}u#Ir<4l%JdvY}+lP)zsE`9#^%vdX4c)slyK z^y5btl?vw_h^nOF2{SEiL@+1T-$yX=4$yYZ0c?-5iTEn9m@AN)m8FBKVtfj(jcz$J z+OCiN&e`O>VH)ANHd_QHR_B{?Tc>t!>*!E#Oox-EANY#yYxYdfsW%nj#bGDymH!x` z{c(pwi&M5YcIG{IIwQpWVvqzUU#Acgn^kJtf@dHDd;-|b%O8qDBNd3$^|(~Ut+82} zFU$hJ9{jNefCY;_j3HrE6O8e_JUk!=ubLGpQflgh;d$FWSVnX|jL3K!8ip-y5#$Y; z0zQ=)4~5T%e3v)mQ}t8jFcG@%%2>H`z0|)-l>xQl=8?k}Sf}No)~7kYpmh{6H5-)u z*`$gsUzZFrnc~(y1yU@o(uH8yLnB&xOpKJv<$Rpa(RTBYiz?jP2UNpSHtAM+waw5M zzU!9!o?Sp`zyG(iw?KUN%Yo4{#W@@oPjm!wcy`#!I9~BX845h0fu2D39V>Vqk@Y#q z1Ov|+3wf~uW7DaeQq5WlcjSUuZT${#G7Ti1^B2ZgRBxt0OSKb~&|3WMKFci=`Lto8 zztAs7y=U_*keMEkDTDu3qWkDBeV1s+TX`A1TLVV5|7tSdbd7qxuIUodzuQ&)wZHT%Nm>+)k?p&Ljzf~#PUuq%5M)U2dIGxTfN?lbYdElX26do|Jo4#HyRmrXM@ zB3_5R9|8fG34&I8@r=nOf?$gYMSODb(bmTSUv21zi@3&~Ju<(-m-uu!16Lg(%;S0S zT*Wznc+)a@g9-W)fljx%AsYys473K(hUd#a?%xp7REex|-IE~n?jTxGt*cO(O&4vW zTp|{A`V5bM_I|jHbt;GHaF|P+S?bpKYx-oM(7YadI$ij~{%0v+@+aZ_6SYC$49so@ zf5nd;+~K0}P?0Yhsy!@B9P!>_4``fFlW7ueN5RHnX zjX^Ylqu=QO#xB6Dinqj-*8(3hSMBm8?*qiFr^ zr@BYU;SVRiMyf7zZWzH%_Cqn9s4WShGtkps3h5+%WL6sxYn*GMsUO0p>*_J3mN3;B zDe>Lu(Jki4A-^PLI6-dz7xREFge`cByBSN7Z*j+kHEOx>(xI35h9LUe4ID^$ z3dq_psWyTX{aF;eo|27C(_`Qy^Zqp%qRyn-{6@!NGuFb|1u1z4QNj24EiG;Td)trD zGQVce!48H|p(Yg)6QF<7j<@!wGWG!&m&_CPRM51SqRa_Co`!cE4hdlU;v+%VBDp$g zz=8@f%1(2lXpO3$Jx`TCFI)U$%ff|K-+HuI=Ac_TbFmjva{KFww{$_K=JtFK1_*N& zMsE|(CACdb)ytbh0C5iEpm|^j{@_AxTz{vk#5Cq+2@8q8G{y7sIq z^ii?=h`^r6a>hCbz(9PBS);Pz9ZKzXm@&rCMz7w2t3HvR6@hD=a>U1?viiBU;lFeY zZ~^F4Q%b~<@fmkft}&!W7)YtH<;UY@_Fz`!6UB#?mMLcp!1iZ9wcyXr8uLk%(6rbL zIJ2vRKLfnImotH8#PZrl zO$NAepymM1JmshvaJ^@zq^rp(_F~>!ur-j!8V-;%e!9+NOL6l74^QP=Uj#0*-mrB2 zdzNr@^h2DJ4JuJz^rnVFXF!S?w z^VijyA+v$}%GVw|vKIvBt%>1Uo>{irGJDKNvT;+6gOg+ZRsZk$O(2`egDQJG>?U z)@Hw38*ygdlvw+?dX_|sRCGL)QS{WNF7f5M zMvzO+zJ@;^_#tzoi5Co&7m`hs`mhk+u(>+4t9k7EhTG@OrRgEYK#%iQyw{&4n`wB`BnmT9ddRz-JMUUF8Az!?{*`U2+{d(m8beWdKIn`u56u%xB8(RTw`aVt9Ik#9h zSZ^v zU*4#;%s75p!(bKI6^I|TvsvA;Bmf$c#xuv*2Ix7xPR#p7s>N!#mgOyyxcFk>j-arN zSp}bya+l{;#cU*tk&srCgN9Q_(O9CbmOB8uH0aEgeiOc_*r2S@p8KSS;R<=R~Zxg<0^JUualYqMFI_u zLFP>XZS~=#*+IOX-KxL2?u%lyF~E#3`rFflf8U0^#NW5g&s^qR%8#@b-C=}JB?pae z!^wU)AZ!BfOmU>*1FxR^#AHs}LRqrCGm*Aj^GUpcFL)qIz%i97lRX49xbvDJXM2`1 z-Mn1z6)5+BXqBFV06f5>h_n1=JQ*Cx)4N3}%lP);uNxTXx#p_R`GuHvpJ#M&#%zW8x4<n0uyIW~E6$mZ*?Hol#v63pN|J6sZn|9gi^C>MlvX zA}iV>G`5&3D4gKRpE<}an_kvdDybI>eZ5GVgi0fjgE(sm}lc1&Kh=p z8SAYcdhcOs)%J}TfdLd3(L27PEN(xA{;*#&ZYeYDClLWEovC7@e}MwXA`^#IEy!)nMVLeU$(pAhW}9zBb#E~JI%`?d)V!%UI8l|j3l z)kRk87_{EXB@uAVNi*jsEd8Wu^5jukQhBep{N^~YVosKU^yg2q)_R~Rj5UM>^jiOF z`|kRgJ9@OA59_QXy^7DLvPKDzNiDY%p_V(%1ZInc zy4r!YiQLig$jr`A)>rF1H_6m;NvF;0ua`6$FvAEh3(kt4Z_y&0eJqsu6l}^htG;HA z=ZkqYM89f)}_YMUiJ?Avz9WKA;wBd?k5AY6$s|Kby~Hb zy#exsJ?_dYA6j<)vydR2l<|gI^U2_%`~-wfQMe`qb;r|N5+Vw-00;Q76y9@<$s>zP zoh*!YEy<9r>%bBU&0BfmKCy*P;%w1QNsF?a?I(cXlMU#`=!Kw6$AAd$0@vjn7eMFo z%}S6c*I(GsSJI`x>Ne`r&-u0`+{CrpM(N1+{bK*6wd0HJ4rv0)Fbrngt_Ln9-bZL5 zDSejCOouD$yzcL;Zc1atmIx|5-ECMIo6T8{3vIpwdAWw+&=~BjmyCwYf}JXPk1I|i zPiAN>5^PT{ykCT=u{*hKplU%(-SaeTOB-jf^2>$HS+k^Hv_ksa$T<*UnNgdgm$6i% zGT8OX^;fOpQ%)KM$3r*%y?NVDQ45l4oN}qX+V{>U^NuPT22Z)coxmDum%`B=gTEK%4njT&DO~g8MUB~ngBL@+i7fFmZO z3fPf)UxDQQj8Hn=XozQ@W7^r0S~1pu5?ED8p4zh$a~}79-8GE8y0$_Cw&^N7mbir>5f;Ny@)?v^Jj?@=IvMV-eJ{DLL}j z%bJD3hAJ7q0q7pNSxy3{B@!-E3PZDDc$U*~;7)fuS7G;HdvhU}@!i<$i;v$hWo1O! z=Y<(o45?Vg0K5BJmY@0zWF|e}i6Q)|j9@%G#$k)C%a1aaH`1f}GU}yTBeJS)R2MZD ze~kUe@R~W+bJyRimocPoc~1OZ^$|{)XbD%15pJg&KArT43a133RXGg12ujiI0$-j2 z;N15VZ&+HE+%A-EL}p(k3*UiAI)v_az$CGahN30|F}=I($W8QcR91HiDHl8j6A1k6 zm=$mazUEI4e+kn;2E6HV0yrVaO`CzIzhp;j&UY*G-eBpl=e|?hmIyR)kg=9WRox+Q{~9 zGHGSJ=y|#YzxHJmL?0|Q;U^2-ctg)Vg3XrNF<@H&6X*qKF9mRrE7RIAOHuCMpDtLd36BE0--<)Cvz< zjuP7Ai`My9Pt*}Y?}GE9;{@{+oB_oPpVTA_G!)`hxM&xK*DDJ}Dz{A2rfU~6^mqZF zZeSVu@s@x+Z&DmXutc7%Q-#G(x4d~z2JZ{s*Nw(&@s0& zU?#>iDvWoLY+zd@0gaI!%uAR%`LsC>yX_UU5_f#SUvcz*yyCZFStS! z&!Aa^o}+){>u>F0_ex;T$NzlSkaVGRgWLVH^*|Cc_vq|U*B0TH6bq$#u0?I6En+i? zvl%}lqs>}iI-sy6nD@8a$&?JU2p4_SCtz+q0}x3ZH>7I9mNv7Ak3lX}r4Z5WYAA$!*XTfO|0OiGO{(`^)4L(qC=$t7h ztoHTLpom&)HMym74>7h_#f}qzB0xn;ggw4Viy{ALFhKIqo*?8Y6iO4i*Lf3lozlv-U;i5p2J`{8Qb(8<=5 z6V#9F!+uIS=Yx1&h@9Lk;bAuTF){0LHgpze0|{Pi)$DkAL%H736X*p=-=O4@z?&7! zn!#BwKUA&j|BOH+3L}?gsiEWCjFfyn+a5K|c@0dqvnoQ7Rl7OUI+WudI{IJq_sXRa z*?i8_^IAviqM6Ns9>!Djikv zC#RQW{f|gT+|V)|IZVm1`OumdppFjW*@nu9@ks?n6;%J#wWhS`E@rnQ&BpZH@=E7i zg;9e_+-TR8NC0%EqNtDcefYe17vNMWg*0OZx94#>Gp|gQSrfT>Udm`YoFJmU&Hzk@ zN`Y60DjF-kXj&_w->``q>EndAEizeDdl=BKmz>JKdrNW`_2G;y6@p+^i%jgVw8Ge zerW=tb5y!`9-YrNgzJLHy&NoP&STW8Ka26CSW&X@-rXL_j)T6V8f3^N>wb24+5Qro zotbL9br!ec?~vl`ycT305L9S}-F@xdNGjk=j?1X^!F;hpG7t~X$E2LM+EgiD{z$>g z(7@%XnYZLIFZ(x54cP?+X3T7bXV^CxA{T#?$_$haL-sc~7pmkj$Q@#4QowGO@;tS# zJo^{-9Vxtu74kckDGILXnjRL0JHRvd0cHFJKl`f7ZB_2TmlvSNF<~(cutwIi)U)r? z9BQqFgYa~j&LFtxUbfTQ7ARdsHy=ZoZB}rkr&VC#-y~2`z0>`Enp_|KrkU7k?pa&j zuXkFo#x>im4%y91w&AGqKZWavcypY#>Ey38^l0rx<8be!%-sft3E`=W-fak;XjkYn z)ow)}dG-T_{Lo_0^UxEZn}@7O+QhR~8)^d-t6NLxro}{{N%hh%Wq0x?s^nl)Z#}=e zD>Nb~VG=2u>C3~>(r<}>og_ow8e4kdPiqRJ3)M$SDU3|;NWD%N%k}Ex{Go$?!kD`B zI0U{(izFip|6_)naS4!96icyN^SV9Dl|i7ZNy}pH%@f31wKei26Ci9Qpa)j%A$8#b za&Ls?haK9IiNC@*o^WdLln+`0d1C@{JO}zxo}I{Zc?W0sBaHMj7&q3TEeWPS+w?lD zYS(PJ%d+8bqif%4-TtvVO@aXuboJA9J6}y>{!IEq7<1fd8@yfxD3rtH<2?vobQ;6Yrc~3N|P39 zi;LzapY)w*D($Ko^8Bmp19&L-ggu;_Ly5{T$>uwNiZbZrLTlOu{|9h0k)3vax=n>O z(x#>JT{e~(Ur4xDOfYt1qXJHldY3?m^f~9KMjo-OBq1i#H}T3YHl@2$A!K;wUBIii zzy8;z2(?E&o5xA33=9*bn!*5mP%qcYL>JVqgn#CZnEBCLNzUPna&nJJes{aAH?@p> zMFiM%HHLAA4O;3*?1>$(ZEl`{N;Hyr_F@9{Vu!MGx>4Y&yrMOvF(0j%fV%+JsAHJUYT$pexO3OA$IJ1%`tY=<&y`Q4An-p<5CZ1w2>O4 zIQ04#rjOXKK^f>p$v4eMngRAHQSn$oY6`N$JMuj%^$I66=^vhyH7()JOrY9Qe9`ote2ki8QT5P@{fGaEEL3%WdGlm*A!}5GGv_`ep z=@nf2S)aUm0#mI!bW%0rav%Bz$6X+wQ{H$$?Q<$F*Uvl!exrOs7dXLD?A#VjG!nDH zt#~j=8a21GShrSZK>y`0(PEj{0X`m4#!J9hIDIagX64h*io-V+{sn*D4~vVuQk40x zQ=|}U_IYz=fYqz@tEj*s0t`!@YrRID32Nqu_=&xVib0En$L?oY;~kxkol%W@w_ZuA z5pyzfOB$j*OM5ut>W@93HHV2um+=S=`K62U$j8-&S}o%)3tGfZ*zSZ8tHI08C8C6y zwBsA?0Nh~uQZdi7Bd+q_52-8uJm8u%x8*-E5&pSZv?07UF{sJ6313&H>|HVE9{>*R zMB%eM7FSm)Xer|qg~p4l0YX*$l7v)R?a|%I`)eCE3RhGHHl8&RP8B&yNaxK6YhoAo zj$icx$MfdL*YhLkUZm=T!9FA9$?hWW(X}9B$BA+UjVMH@!kCf*&La8 z?f4qhfjY#-GJ-tWz!M(2b(YoNo;`JDJ>RN%5Z^tLg+GbRa`g2ihmMCUh_0+qqcg)u zWsN*9r$uuSG1g5vHu7x7ghi>&0G?as&7ECx$as$R_RObStOzD#Jhdzm^}{o)Fzx0` z*!ad$Da@H6yX;*0QAS`(kZ|!DQ1F+szN6YyT&KR7oiL8_Z={$)rnoYreZR~&|D)FH zWE9iq#VGz1&w5G*XC7%t5GtM>D)Q_I1H4xYhQ*!Aa^`TkmUW|I$0=E18$%SP{ zbnV)2hq$-U>4yx|@yb_?#{V%#p;@og!@QI`rjsBo`$v1h>ctKUa#UYlGc_;q*LVZC z+ak^u8cm{XE?=|lnY}ICPhB{+%~iEly6KPlYn;R69WEq9ti2*SV&`V9&9^ZD4QA&E zt+@LHh%YxV=qt8fmnXM^&pY2r1k*FlXw-9&>DvW!Lyv;y)!weI)iUlnKVI0|Y&Va^ zy$Gw$xOTL166A)ubWM+Hc2(QJ)pRq$6v>k;Vf;kq;EIBQgmCW@m z$Fm5+2yY7(4hWtM!+;X4{&zbIcOxX6L(e{BbE9 zaA8W#Bi=q5>i#S&SJ4$uzrSfd$rNtoG0lH^78g5~Gt=7vi~yZr>sIHr0`xf!*5l=);)hjYdtO-R9J> z-5W$?9?^d5l_7T=vFSh!Y0{W9qwueHIXP#X`;edFv!?C~41vvb zL)v-sl(YSfaMmj4(>3{WlBJ~@lHN8h=B##h=(=m-BL3=~wfQq)U* zx=1lUVsf3`_BrnE$*XfLk*YJtn}Y4R9+1;ToX2b zpxs4Y2Y%NaK#>5N><}iTdo1z)S;L=(ic6P4rmS2@aFr>Kqe4gZja)IFD z5cE896DABxa0{^0mv;f{S#Psg_sJ(_Yn4?!ck}3%A;j&ER||AwAYBeSKEaeYo_6%z zBSAMX0w}=g~%QG3&sIk$YUK_wI%>VhURET%l(PZ zqGy7nTvfVW`=h2S5r+J38qb!Jv`(V+kfAF4`=7NzfY=zVwr)BW152&gkvo=FIq5Gt zhxf1(C|+b7BC)^K+RpUUN=hfF{p3xHa5=O>9W@fVyTPfIocp6pQ$L681@PY~UL0N@ z&evs<@GoZrYu|S&%cB89Jz43ffeQeaR)TT%wMtpapN%$ko{SA0Sgk4m3A&KIar^n#S%#?%8d;4-%E$4rU;22D;TKdQCO+PQVvzX_ zhk60Ym-=n-ExUHa+71z1!HPfT2&a`2l9opX6Qgc_DcYDpr{oxWNcpb9RYvvI`$Ne6p#eceL2edtuaf~`+K zh#O@89*{SJPx%6BtvIab-*w6TGu=U&9t4PmCg@;M;lLOCBe%n@jy#fT+~5DRqSpJq zB1CE7{p)eHA~32%h{nN#p37#epWfR@GNKF~tlwQULCs29D;rSs!WlLm|9gO%8oT%P zLeufeM0hcg;YVz&e?Oo#|)ZGfrroc{aH{0o}r z0E>Y29~vsBswC|1R(w-y>}QyKc*6k9)Mo(IS^(@vqEwS&%l+vt2Y&QBN;|JXydT>n zPGd~~v>15q11*@A7@%WooK*q%VBIq+>{LD*4=2R}BL4oQ_U9F$cft<46Pl@G5p>w6 ze{~8zB0s^PrBTEL&LEk8PEUU$NZxqcYh?HDhGr3>CH*AdFrs2k3izPW7n~)49QHFc zATwjK@BL}2fJ+pq*|cP$d9sl8%fG}W3JG6Eh~P$8G~n?pepos5KXeZ?jH&SttbMc(x)5r=J!3EYM5T@DfY zaDVOfQTXYGa=J(-9)SNz0y*68xC#s8^_V_zv0U`lYEIIq+@8!hR!aneM{`vP@8Wix;?2SwfVf4el|MY)3>3_eo zoidW@=KVU2q#2@ClTG&itLb8G`dAo%M0k_*OR42G@_*};0;JG?$Lo-{WA=>^24I&k zy=(f{4-px{!bB5i4b;H?&v*XcuGSi{_shvEMHo@#2 z-v94I0_H#v=!5=yEQ^n4|Nnmo$nPik6(T6-!+-D6|L3v1gNJ|15(BP%=K|dP|32gY zxAXWvzY|_e1QY-duax!A66(L^rT_VD|EGUp!@*&_k@3@t68NuMPGklA0N|~?dv5zE zDQRhK;pmiVoK;Re0iY)xaR-y`nU#t`w|$S;3v2Ad7m!<2JZkq_M2W5m>ff3-z2GOYc_H6bAIRx4dlD%2jFpo|C~ z>g$fFLV@26cmeVsP%n@+9*D^gBhr(ucbD$?&-41fe(-<2rXL}K=Vtot-x(nw@6hnJ zzs-v>9kN10Xqg8_Dj=XEXz43*A9Fnef>;XffvSR^et^M06azGpXM42LUcNh9Y4q~u zf25uSMaW2Ze(7@au!TLLd)yy7{piodSH85Ep1@43`Dn3phjuXvd;Sb;j;T~_FxCZ` z{CH)BNqc z?fs7=l7mpCZ$MCVceSMcd%zdR@of#{x*4lp@Nr$z+mP-z3j8W2Pjc&@weAo_h^NO6 zKc2MtLY@PU2HQcCbNO+8nr8r)^?V7)f-S42L&;d}YY_{XcY+x4@1kG2$3 znCGJ^R=!*+1HAa@m$@kJ|HAf&6{~ ze6F6^zxGc65#B|Lu<)$2^(R2cHpAhK1v;Dz0bbTq*u8J3D;?a+CqV*amxu2P+yR6! z=^l|aC-$FHBYY`=7>wI9En)C}?E$#BUyj)3VdthqUHDMAZBg3(NjN@^j;{Er%2QQwX+j4gS@UjR_0q|uU5Q`);qvJ&~oW$E{ zH^Y7GbpprurQ#Q`*opAmu=^XBv;-jD8`r~wr}7-=eE@(Q2rhHZerv+Ayj2p2Q5ev| z2zrUnBnz1KG90ZJK3|iz?3`qFP8EC;x0uWN^Sf)KU)gLQ%Dn>+IA;(obUtyE0V^@sf{*N2;LjnCfD3s~WEfo$Rghx3VuHsmiI7`QE! zk4Zg#fMVY(E<)3%PXX2@fQZ?e1ZuIJ=%uFJYuIzfewAsyVR!I^IL2j;48WWnX=#G-O5Z1 zm8BY8*QH{fEWzGfHL{NLDx%48k04h$mJPs_`780{`?khB5O=86c-lh*tbay+SQaV; z=XaNNqVEw3RIW zq0MryX7^&`%4z5MUmt40Uja}0q4&JR9>!^N0h~$x{MgfqwBND>AgS* z;K#%H%Y!;#2d3U+#52_itxc;!KQoj}Skd!&-N|0~OXX(e+4i7mxjAsEQp>IyThtG| zuGkf;O&owCp6-ax)Nnkjo6Tw#?9%lGrby{Kx}ceVOlWlmT{s??rdJmiQ2z1gw8hH- z0FUQtE#p&!JS0h7-~ZuFx4N2B&XqAn0fWbE4C;2i9CrprB9Xj~ zC3G-LF*Lf7Cg^57k;80!9>)l%W){N69r`0#eP6qsN|?{rROWDuTITP*xlDuBJ-5N# zdv?WyO&)`+y}gZdzO73-@|Ac6pc65L;3LMf@53PFfN5QJ+;p)H<`oZNez1m+rFm=- zl#k|lI(j{dfeAVb%b)(;3b~!&i)9Uzu+yB8@1n$vvzD$ zEylYN`UUJ%%0c>1uqcPGSNH(`NJ)-D`m%u3nRM9R@h2d}``}6NnSj+WrUe3=Ybr{~ zSJ9r2NPQE=gI}+Y1Sbj%5P$|?tm!r8r0Gb?Zy;D|Ve^h8lIjy)b;MKFcBSbtsSCj9 zJv=YAnC8xLJ~ga^dHW)74t|U_e!RP^8g|d$W2Xm-P$k&c`@b>WNQ%{Q zCTfqouvdieRYLRK9zFK5lkSJ~*zsF;9+5kN>Blcm*HzWhLdOfE?)hQ0=j%OTSB={a ztA`Ind8)-+S=V2qay|~h!0V4f_!oQ}mh&BHqsg|avDeEUz4H9eA2!|Nm~^OpkuhqU z-mn@Lqj|z? zmi%5%&D)0jrNTSh=JSj6P5pZL3WEc&!gDhdRG*C7etJd$ACI{ZP%9EKUE=cT_OusQ zcL|8H$lkH+4vwwrPT8KT-nP<@GR+qcL|-85`8lS1!9L%*&hW6x@ z&A6tHzXFQ$nJVEsKKk+`2I^#4@`%W*C_R6WkE=lBetj9cc9df#1&>k{mFu(pL3sLJJPM!Hpa~@jA2M>dw+Ep4W#+F9%*qAHK7rC)(-OhFx#$` z_kCfK^b>zHZR7{&RYZ#`?ogF;PVxpmd1qe%xMGh!Dgqxy+1OnciS?5Jty2NN%-&|XYz*O*RT zx-t>!FBCZtC9<2QHQh}JFV76>tg^1Uq*ZSZ{RVR6gOJvE4{uI!uBtP<-x0ox<}g<( zR4pz;+jF@4!s#u17iu+IQCai~p0(C}U)ObC_dV}u zM1p{`rb`{{@D(xq-m8cq?xT?YAxZLpY@8bE(hQu(^&H&D_K%)oBBA>&J9In3UmfA1 zT7o7N3y!@dsMQ7|-g6x;n)w1v_SF4LV=-}S?r5?8Mmps60Zh+1Zu69r__b>=C7+o@ zG1!Smj{!x@ZyWwjF_EO6m~s*Q-E;S6Ok*!o&@ypi*T;rqtK9kG4e~c9mpsg?%&uBl zdOjX%ph$Kv_EMx2*YrfK2Z1hrLuRc#!8A*r+FS$2TB5zCny&7fIW+8BX>D6Pwu>es zdrPkSR-;#GUZ>*79rzrUEEC3Dkx8g7ND{xiw_q5 z!ME+6zWAK~*_{N!tohN*l6>6_pSFXIq*D7K&E5nXao1#e=LIX+ogI{clZ_Cpe;`C^ zjq0e!^~am4L5(8l2T2t;7G1ryJRl1it(q5oU91}dJuYe3GsFXS50}Zc>kfc1T_dwz z*2oRe&ST|Tf0nT2SfLNm^f+a+|9)_6at+gfpNyTW{(^sioPff;FenQp;s?wzL#Mb2mhF zI?grfs~IUob;NuOEI?md-T{O!b#D}$jGXclr(Nr_f%ll%k6zOQ#)^iW5<;s!!<)A8 zZ&}i8Bic;Tnj~GtEf{)>GlN#p1}XPJ%5e?xCUfYi&#gqwViP&xszLnjd5^^r-nb6T zM9M0+18j_6G5-!|$-6&+0Mcx8LgT<>yAIB^@EQ(1Nj&h1(IDAUdd>=f!bBXt*#ayR0 ze@EBw=k#UFi~{+y?ascp%hz2)zQ2ntZIT43;Psl|y#yv~*r*hatO5n|E zA;Sq%!i(X3cE>t~oqs*cZP!k%T#*W}ld)lMV|>;K|$u@ z2~V_WmFINz{BbPTa~lH>@imT6k%-Y%trE!vp6?Zw%(q#d&A%7WFBvlHYu?_`S%9YU zBZko?g&X5y#ec7BK`_ocu2u~3eY}@q9p9^G%g?fxYm?gVPTteVefo;$y5Ej&KLe+& zD?+s7aJ;P{XZ#_L_wSBj2^0Lzlj*+0atW!wdX#%tGJLY$J!bH*q!F-lSB=RRTweMX zNPg1~25fj!SuTp04mDux${5sduu9WojNvt*J;u#`&*(>S)i1K#k?k6XjOmmu;=|dQ zZdS)D@~_q_mD3k(Glw-6x*hLR)W*1VZ7uW-p(b|M=rDNYdP9|RJV>QCJIf>FQEXcJ zHHA|CA^J;+@DO@P@luH?@|sC|?}H#&vUY>v%l%EuCMGv$ms zclinl4>jRG0e`2Mbq@MG@<*HAsv^5?Wh-hGt&x}b-Kc))c>CFi}Lt|{wdu8~#UzsNNvcI~bvNaqY28Fmwu=J1Q0g-Sq2QRpNn z-+~S$6zi^vniX4Db|ev!&ya1|{6MdigVvai*T8qBMru>8+B^P4QMn2a>Z?d1NWV7un0#m>t!^#L!M-6LsRdI_S2rgk=5$Y+>OCs^tLiqb!`|H;~R2U>qf~=`=|M)9jonl)rXrumNa;0aP2M7*R z>?v3cj+qihB(*G>$ytIP1%!xQ0)`+~Jni!@5Eeg*O0|3rw%yCCF(sfj=JNoVpiG(dPN(7$?6qo5lXtH znN>JeSc|hk+oH4+$ePMlHp&wmcyu%$B}}N0vnUn!X-vM>JI(E~MNeEnq&Tx6*)*bP z=8h?-0i8=7S4Cq$#Q?O5=L0-&G>*)o%z7Y-<&xL}mpCPZ)fI|a(rU$KfI`%z2#h=0RwW3lPD+lwEPYwS|4olIG`Ka}4sk`}26 zGgIlcVn|D`<+fH)p%ww(PI56TC%T8jBN9w4~8);0nB4fR#O;n;zu!9DB ze!KA*je0qSY}?^aO?GN&CWJ^OlqXfM`TBE*d?TNO9i0a4ZKBk6h$j zIEmX`&E>&uvDfp1+k+vlVh1j(*4&2sZhrMfVm!)Y)dN_}GJEK1jb}({f3$rcvJ2SR zPH}2VO`f1~Ml$8$mqkaF6CYS|Gr7d7z9XFO%eOiZ>2@3nwmH`6V}8H#g2TfC_YWzj z9`qQ6fpo2}X#ic^dFQ3U@KVcO-)EV#_mi?xSdNr;Y~x4TEI~Nz$h?B}B_v-#vzfME z?ah!|hbCxU-~pXr*`DvHHDCy~=;zCXm_^v?lv|DGl#hv~P0saW2U#8GX2;cfXH6^SxXap81(dt0;HvDf$NXAX%Kw{QRlUd zK$-_=r{2aZC}o4=H*txcJuXmOWB1(;O}vC;-ztKDJH>3_Abml))$Hmwr0)bdD3vI& zcJElrH)wcRIv!E3b?aHkaxQDi41U-(w&NEccUQ@?t|symRXV;Q2w(tE-IAFDIE+yJO}!%7ar@)1_anxwTbIEGbEZ$H?~`x$tus%<0+_ zg;kMrlrAOR-7eUa&lV}HNcJHhUqFK_mLB6K4>AKttU)a$tuLChQi*(H`hxEPe8=67 zfMDXH&4x&+m~~QCW}C=!MamS6XdDZ)P&MVpK4$Ww$(B2Tpm7xv|MCKwUr4Eg;lSEb zL_^6YkVt^bw#ty9ceLEZJW?S{Yv5b z1j0OXh|s0;!O*JmA(47hlvQ8j3)^}oj@XIIY3|FPYv=5JmshD!ZJ1ZYU+{lzhF<>+ zEW)ooPCfZvxCUFHYV+u(`+FCC@u3m4LR)n=D(|b?H2pzgfoq;l`Xy}^jp4^( zVz4I3p34%Wia9)jZaaxdh0Ebhjhq3E;?*l4IyOxrOuL`iHZz>kGBe4hwH!m;{iEp7 zKy#EW`L1gMeW8>!b-SY$|m4F_bvL|1~K+yp+Zn@ZJzj% z#sYy!S6Ob;c8ikaD4wQEkKT!_39tpN%EDd~UuL%k$y?k;L1&V%K{DS%#&~#EZ56=} z6n%B5@W@X&ks3D^Oh}jX(A|8D$I5`#x2U{Gr3PF6X4%^w2V3RXv`0ZJ*huEoZj2zw zV7=nLRFwlpAG_gyZF7gd3GVA&vC+w&zN@m>pJN-VHb?Dl%q?f>6=jC11~rV2@M5>b z`p!v<5)&!*P0=Gfw?Hqd(G|@i!YjeuQ`Cs|*%4sQ#0L6m0@QT3RhHg4`uONV95y{5 zif{MfLT{!t9`mXu=5W$7d1wsd&U?-g)SMQ3v|Pcu4Cb@cKU7IRI>}pjeZxDL`q@m; z{-u{2D^P(Jw0mLy)(Z4+GS9VM=nCSl96Ghqpu4b1GELI5Xev&K_)%^9=$@y* zWE4*U}iKPQ*2Wzl03q|9Y@u8{s@naH-yXR=rt4HMGCG-W*rW8*>Bm zgT26MNG}iG+mcng`n8dgmKzg}is7R4M1I4c9kXlTF;;!&qCwq%WydX{52k+lg-@R} zCy*&mCIm|=dwq+@R9N8zcC}Z{c35C7`v42i9BUD^nY*r6ghN4`keZPp;B#xMWuJ}N z;!dmi^vBU|)Qfy31x8 zZWy%JuV znWwN6s1Ia|f7C`Wk1#Og@wX7nd68ABIaXf46w8`J5gAWF;<0CB?iMQ4Bu)J~b}n3Y z8GW!=mbWu8;0PIP$-oe=>Av`~mx$Hiu{<(;)BeSpMP;H|>+6zR#ZXE;oK6wRs8?NT zlpKOCdG7OZbEeH0+cIN53qm!S3n%S)8qxrXDK?1@wE5m1z2l~XcXSuNW`~4iBU>=f z6dC_*!}&WRBYA>@>v!w!Bk{i;oS$(L@w}F{{G=Rt*EAj5&s(N|B*uSWKrb8TZv5=r z+RL34yLEb{`Oq|3rSkYo6r9pa0SAm$3H${aTF42Afl>m0z<`{)hs;CuMR;i<=Y*fQxCOqp%1EA?rTlUS?nYC$Pd%u^rcq^ zxz($!GWVZo!DhhW7N38g$wdavZ83^0Ss<|b(taF_!4S^A{?tg7? zQ5{W1_0nZ_W31)NWHYkS@)f8fvgnhx>R>{! z^e9M8j3zx!de|jF^>8?+p3s=YZG>`$`0?6k+1Lb6{D+UQhWc{+xV3Vqb-h6Y4!85D z*3LTcn?Hq(huH2tS=;!6y!LrXl;;|%0P}wNmGPIoEzbc7L>e=rcA3=&b4rKi7}msK zcjbn-^<@5)f?)gb{bX1p+D$A=jBRaZ@}BFFd^9^Nb)xI)xFA-y-|Hs2Mmyz6J=N8< zt^IGlZtoobeb@La`l;N-CmRnsK~B4c!g*x=jPa)?$XjFhf`}-EY>Eke2TG;G`#>Bv zt?0L(d{c@bVgF}q&s4Fw5#)-oscL}810$!9#$0yol3}k8^NFzpTEVpUJ7em58%nC7 zC(^va%1HgXOvh|u#+TP8^paoyBy5Y}$O9FX$m2V3R%01Ay;7~r_V7!T)Z#{I0R1t1 z_v5R-pH4&v_VOEan$;=Q5OMDzZk6jwy!0TS+s^W8jztbD!ijufrxA~MIKob6P)Xr} zQo>FdVe+C4&-fPoJyinO{d@g-=+t#)EYa_C0n+%$F6QS(ylMK-v4? z3dP%q>AYcOK3rp|^KW@iZ{PAf{Q%3Xt|=@`_xw1Jb-E83O`xyGgDmC4@MacbsWn=%gA5=&&OhJ}?J^o76yd;*%Tf=$$>7oc^XoYX+N_I}(Df12GKl-t; zkbP-?iR^`TOecJ$7(nMzX=-1X0D1z!_GKf-2i1hNi}6#2fE^VoLa&~sNSs!{zDF&d z*Vc|ZJbzcuRvrgs?(^2^Q9(T4c(I0YO9)hfDMfDax#k=51`hth(qRTF-BJ<^ssbzj~xPx(5B{D_azM{{TFEY%&BkX2f>Fyt0 zdp#zf0tq>UG)V&HWg!3fl zEpPR`y%=aF*_$=ezXiWWKDvskc~lZ&$+q|OVMDePW9y0eh&`IJ{9aZtR|_Kbd%;+F zpgCa?W%ISnz8>Tof^@6r#U|PxCo_kN3ZIt;~3SNRaB*&JEof3ENAjfd0j0@hW{ zKfcPwnYU_VQ92(aU9y9)H&B>Y$E!Vb$J<&j+nzzuihob?KLgM|7)Ty@pF4t``<2Nv ztP?aiMP2Egu3R2-Z~@Bo$h72#~{D%7k8^ckgy|{@ACH zS8p`5P%rzQdb(n=*tE&C;;_I8SJlalhu0Y zz!#yBphCO@I1Ydimnkb)ioJ9~6QB7Ocj*IZRFEcBnaLMRT*C z8C;Ki+<}`G7qMv6IHxiYqao(gc!X9xvYy5_K-Q;j?pL}IS2?jh@RGz@3}Z%>RCFxhn~H{9=i#*g8cF0zj6m~dTEs?o3 z>U4JF)f1wb;j{c13(+<-)GqQARBB8okg`|@zH{U&)e%ezueW>P{s_^mAsqu{RbP7+ zv{=t9A$fD~lE)+>l}I%Et$+`|^Qa~D@bE|!z0>G>nwu%!2kt|VA73VwD=0CZ!Y$}E zEP~47H366`yA+~>4p&&G;V`(VQTE!L{LdQ(Ww^uX1t7{&YDuT$* zT6sTY;HV|Lt4)PMe{fvc^6kR1G6HvYzgHv|;a-wxtR7F7CEJVkq5Y2P>nKc{Me!w} zm7(`ENpib-;+XjJU6vKXcPr$77~N;1IVxHemAUw<@6 zKp?2C>Eyy3c`Zw`fV#W`l(}T6qeE?EaP(4-Jo#t6z^Kxx=gwBoAR?c;ZtV^4yl)lP zA}!e>Qfzx8ON+0flvh(#MN_DS6Z~cXXmb&XV_Zo0`woj0&ya*BAzL6CE>c zO*lICb=m`%PrYRd<~XPX zI!~+NLnn{8pRjSS;!PeicQyl+6r&+=t;$ax&|{YcGf2F43A)uhW!u50cdOEBFw53$ zOrrzyK>J;y?N?XgH6Dt=a#C#yjl6*6=h&zGBfh+BZJ&*ss9M_36O3Au_pBs&>PRZ^ zT(`8I{DBt&h)`Mk82NrT^%509?_0q1zKmAT!FO2J z;?nBOc9)HOO{N?sKy*tD1E&;ph$33-M*7AW<>_T7vp+vha}S{w!gRy!?|rJj(a@>UbkK4cX?i^Se3Dn+Z+fS1TF74qC-644`? z0*Kr=q|M9qjddf&@n8?vB;GM20Ev9<9Nk$eqREVRDO`j$VCWWueSX1G|6D`fm24!) zL-DshTPHn_elZfmT%X)jUhc%vj_{1xumh4mszGtxxBOcm{XMET5v|o_O|x!J@MFVc zAjBJ$ph_BQpxW}+a zrk%Gh5Yv@Dd<;k!ol|@dI+XE>(GpdiIaCPr0=Lz(J!U!krs&WYr1auAtTjj6co-4M zDB6}lRaFp)T^ z*KZ=De#tX&SeY9#R&EE`MJob*O_Pb@f|2>sK!LHYkB{>4Ax4E&tu#(=qRvfVx5d(2 z80`UQ{5k)zE{JBw7k8d%YjX|XN$1Nzv>%}^09zj<8qo6ry->68>zZ9{LBe`l+`VC( z6M7t&``+#(fz>vKlCsfg(n z@7G{6DF<9y-me3V-bc2Y?B-vA0O9d8vl0p`*^(<>xmX7Ed^&g^8NdHtOXd$CZAvBx z5L8-gfgM-b6n`FXZysohV$L%Ve<0=G$CVEy^1HOelkKheHks1~+LNO$o1>SPHHiXX zn^s}G1iaeWN}(l$uJ%BBpxv6qsB@mUOFZG+Wm|HVV8@QNeLJ!Vk2)aKcN1TpsB5vy zJt6fGdma*x^dT3C5_RK;^S-LBQ*qxd@-}u};`^lKeqmV3-^by`_ z{jsY~fLOHzp6_CR0T=A>GfJ;We3c%HiPNou2P*g=>kl2$L@U~k zjAy`6Uu&yM9^tWiBoL_7ZOFU~YmVz-%ml4m>?nvv-5Xq$c6n0S`g;fo?i?=jY<3(F zLFx#8ZZGfW)D$Ssn{P&I89-w#rv`$!u14RKs(%^#mcM7?YZrf|bcwg^A)kq*465+N z%i9i8y#li-GiCGG-Q6DQQ|f;9l4OopoV{Y#tB<96ng(cvXqGdv{Zj}{jsyi~@=tT6 z0hAUbnnBkKO!lFZ!U-ejR`C;E084cZo60Fd>cScQ-Od+!Y(3F}Z_S#>#orFJnV{un z46Z3rW!`ep+pD?-DImaeO|B>j6l*CqMVgw^@P1@j87n)L5>T*irV*tLOvQK}*`4>Z zV)3}K=-AtKoVDvpK%U{+%07e+<|itr5jKmW`DjfM4@<-3UtYufpk>V8jW1hlB^E!l zsF{a4e~~7f9kb6;uu~U%a>~0j-)IC$?e&f*4s-J|-lYSB!N+8dh@-OkT_(750B#gXgb{8q~9b-I`Zm0 z^}r(^ol;B9OaKKnDa&r9T)fskHg#jgpsKfn6xErRLQv;!-PwT>A=b-TrJmWUX5~wt zi-*gOooX3;#6+Gu*<8H(`b!jT0%H;UvNE+vDwAiTAt{j!rPZ6Oz_NQ`W59Of=lPx5 z?G`A}ogO!)`ZkFARN&!`7-hb8qFu~0hpv5KP{-3GG5OBSr}y*HGiTic#+r#S>ag_P zRQr9(qaLj_ESNrt%~`i`v%9_YYe|M54)-x_t)yRXU1QHcUkyhlI<7wP2+h@y4n>A`5+0mOzibqm!WqCV|ZXH8#*AvB{7}4tK zJpCis=4Qt=)XfJtDXX|Vq@;L6$CS2j%3QY(6ce#hJcO2?!X|1CM3T6-6^aaRB8|Uo zSrUcxG0+g10LAF#Bw~#wUJj&6Am9LrNe8TE23S z{xS_qRy7xn(sHulrIhn|S;`x2pQ`d2{R-65^w`L{D4vTwHl*u;9PSYcI_-sJC?pIX z-b?0}E)VYBNjEKqEDu|jJhe=H{wyWDd*?@$2zQDgawUzMP9yIlEt}Ao1tjwbOFWE( z6R?gWvYNbwp{vDBWbxj2xp(6lFvnblyMPhtbhK(9+FNHIm|1gokF`t<^SoeT7)%f< z4Xv%mHqc>us8TRhDwxOAE?YfLp5nXV_q*4cP&LpI^Oq(C_Ws|KCj33G0~?t#LiwIx z8$!jGFQuP9O^)t4a-W~Pc!Bsv=?EE*Y3Cj4PD-5`a@g1TNd{!XWo4aF6+DBE1cv#5 z^7+J2Pk$?!^7ZCr7tRA4w{DZKKgFprPmQSmjU)aujtLmU#lQT0WeGX|=luSR^J?(= z5GPLpAh7fMQEj9&4Qq+9mRD zFV3dd*+YJ9%Hb5=7f~9-VsXz1h^QGhrdyz~_kW33PLdjdymRJgk+1FwXf~tAWZZmN zza#EfqV*T}`}h4%>61Jr;nJ^l`ynDI`}aEmKDa;-yz4Y+fH@llbCGX#28zA473Fc3;_U_>*@OyY{WD#2e^exAg1g z{{$f(0a@UZ9()tWe;cviC?Eb6ov$*T&Q@mL_o`d} z`=0;jUjF$Li4wRZjj(m@pD+HO7mq7|<%3et3UYk(f8EPJzK8@vJT75cl}GT1J2oVV z+pWvJXIFS+bEb<)Iil_ z3Ba=kLcXJ_?||z6Z)id&U(<2 zvS7t{v-#meftmgs^_{C+dexMxImg(w2D=kjkuG5c%tSa}zfLBER`9M|BvZJ9xA#r{ z2G65vpjU0;G^kjOC0Ww`-H`w8HIizHd?Lz=9Skb$!chpGtK{Y-INEzU`Q2EO7AX0MagIfLq*AO5okOXm|>;n@%-yEq~qt@Z)4C zm{X11TC;3CypG49JYd{02cR&t;t*Z^*B?HX+K!FtdCqlGzcn=)b7t&iV$PGXs? zue%h!G_o8iuF7~EersG$87SPZ$6J4P>Cmh?M2bVO0QOb@;;Y(a4`@aU4WCso$0g|U z-Yb{+4>M+pE(PCDjD>hsM48pVWdXwu zYjd-?FK;iI)fTMOuQr^BJ%7h}ditOstK*hyNt2#q?6N0^efkZZaPBq*uIR{WXX^yV z$IAmLRA<>_e^yHjFKE7ihctkw^rn!vbkrTp)4=g9pqo(iYQIi1-&3l62`<1@xtvfE z3!a(4`oRM1Bfl$DHa<_BPz!zL%MfL8kCWX=7|JwMG-k*#dU^P>)Rh||x2bqcw{$$W z7x|zGpbo4xf~n(l9ox|peS_P-u{f>Z023RNAr~1h5mW83Sky2srgd`soF6_F=|@DP z5HyF#0ENw~LdZa%LQ6i^^oT9x!_U;D8O>Z(9}gH|*n^UIbJ z(7f*X79Ze1sJLH;fiyszLCyzcLKddQ7KrWfaXVm}ePj~3LCJ%E(OY3pFBiiOubF^9D={kojdaxu<2ogkYhG_e)ZK7ZQB2u*uNcq! zus2I?I{ymhh@K||$VJ+@mIUG?()YM+=1De)uE%qm#ycH<3>xC=UMUBT2L{x0>%R8A)X{lJBoH=V=v%-$-#^T>S`S%f(J@x3G70W?X7?eWVgi z0dV>fE8`^TBX>g?t|oR|nb#ImB5|D~@iZ@wJ{3F@l>JjO?6fwa6!TBf@C~#1Pd^;y zyMK`QkJq}CoVElb$OTp@l4i&QnNxi{;A_<7WA9qV65rr{dYwy~?8Vrp>mWUTW3t#^ z)+Bjav?5mL*7lCRAr{ywwo~{JJgzRaoLwGq>;=uDdm3-SRD zHJiB{=M>Aa#j}&}sT2+1Ja{yiuQ5h&2|OGc&F|?rXkdEwO4R)MTsSy5X=6L|_wIcw zr*6$?Qm1B*0(@9aK{BTOn)owE#OSH53{C4y)U!*U$wSZU=qT=HH%Z2yFMt*$t7R!# zhTgdKT#-Z#2;mFLCbW(e8T#S}#5jkPis1yge#+N9ZyeH`~`FvDV^YrRJjQkv@P_=fSKkAXwdVBX9S-hCYtoCfAtcOU$UgjhL(Ci z20=8>Y&XH<5pGkPPw&TAg9#Ba>85qOjb@;yVZ`^Sr;ei?Tv|~eMVI>?%QlvpOv)%L zk5;A^%B!0g7~tWF2*GNQOFYeeN_EO4v-6FFyYpP-v*~}fvx;H~%~D`-G($$6vRtd` z3K3=2V=`N=m#KN5@si}}ql6;5w)!B`%|tmGH6b8doQN?_xPN5*{GH+u{51#pCv{|$ zm0Lf-vz$OPgS*emg9~@Ycq_Bz`sRuqW>p{_v7tgEj=L+huQ$}HAS=~GcNPbhczW(z zDMZeZo(fBEt2Zn5mT1}U8)L8RPlv8VAfwvS&lI0|J(jSM?cRJbEko>R@ZXvB9>%iN5=6q}VKabJoBC zokWPV;is0K9x3gQjS=kY6ws@2+R>d=7O?FdR$d#g>M;2-vdBqn|KfWnn+*k`a|Zl# zn^-|dvz%u)A5>Z%anU4x*Izpi|FKK`$FB_R z-uTp85>iu;OTNwqqIw_0h;9g4ywNOm8cDL!iD)-ah-7|W^9;|Yv_Hna@7z_yQ%7*P z_5@2N+rrzr(m7OCbv^MkoRZ9N9Y%pfG!vAfB#x*~hTagpn{9VvDKobH?PyUTVSCRu zp(NGhS7LG6#qu2W>MbDDd;;0ia$7b%n{{zpj_BazKD;oAg^{^^2vTXexhFOYq`_R z+3YzeCV`I`6d7bI)1h3L|67Rm=a?`&YlKOk7y4t|$UB&dJAmLaogi5veC`T;m-TKk zjwH^86u~9IB)p&d2GDucmyl#|kd5{lE5jzCq9HL$LQZ?1*6uc75=6O4MGq`qR&l}T zK#v*2j>`S#6cgMmVI2a~T_3(S8OxK1*IQT?cgGX(SqeMN55^^4me;}MtRE38jS+_( zcWaK!g2Xlq0m!lieyu*NC;=Re<~>N?Oz z{@Cfc0z@XSu$iD9DCwAsIq|+;9YkqR#!xX^qVzC2yxzN!YkMkd>5x5EsJEo& zuorM2CZXan%U8rMT2EJKX&!Htcx=ya;8?0Q=*MY?I*Ie1Dc*_qlDk9ZDibyw zaNln0)9{0Qi}$R_vdl?f0r}T(A>yY?_dMHO1edI!4wVR zR)wBhNQE-1wYP2|aBkC!6`_#z%aww#A8&305=U|j6{0M}Z0mZ39=b%f${BfE?&r>{ zF6@{6ORL2nm7_SG%e;B>Mp_}`J*aR_b}Rf8b?NeDXK4KW955O?W#C-Wu6_OaaBuqJ zNTp>YQ0L1n3Rv6*5pslPfsS7_vF2}u2mg`JkPP9I&4*%?;c1opubXMu!)|?-iV$&e z5QMJk`nH=3GF3S(b!v0V25umYTb3bI+-AYOJ5a}TphpObVXQGL-l#Q^H$WA}@F`q} zYRQJuMgt{zB?jtx%dLEQhXiDQF};}zasZ%UbZ=!BKw{@pV55d9AH`JO#huH)>2skC zz)3T+@)|Rs6s5NibzyVQI8?#!^1@fq6k5Szg%H~Je^y41Who`JWli#TQ26-E1AYck zJ$2u&hEeED-?8I7ne=wcQQWFpiSpMt&v*A14>#u!j9>{zb0kzeer>ww$@7qMlnkC4 z0dON3S6YC$0rqWoWQ7~$gCixV*x0=|U~RHgZkq;=0H;2cB_52%WaE!|or+Zom&Ken z$JK2>%VOH|lZlx|HTRwZWFPe)>aFs<_hxaYf#OvzefFs2n-qX3n{B_=<0hrxy>AVg zpOl(@<>l!E8h5%h1wuzfqNlx6WlaW`BLL4Ffk7h``pgif2Sa@zH^X1$uRaXo@*Ca< z;JiZ98~Lyl7K*Mmdz0Pd>5Otf*G)J1AQ^EpS!V zuUxMvxg%4jRd7&^AE#zR!KELOT<7ZVY6ptrqX75kS3LoJ8?$p)J*!kt1Ty}5zlHLk z97LM!z*Wrj`0zZLI79u~;vJ;5m+EL~6V-dLcS`f_wHWj{7%<9)Z0cxKJ6XESg!e;6 z!af`;9McIpNCOPP6gkFfG*?ER0>?d{97ZfQZy0Mgx|OiCIM9+O3q;!{ZM~V?uAR>u z8UENOIF=p8ZVsxaeIcmgm{6W}jkH`S4;aPk!3sOtjs*@LdNmLQ z|0{HvT5Dpb;>jh-9>G)*9@GN?d!Kgmd7X(1LZ2j&DtyZtoo&o_l#@40q{W| zc2pLy%0KQ&EVh^G-d`2DQelqNcUjraE%v}dpf$!Vp<_!1*>tUmed`O<`s`bF=8DyukJ(U*x`cK zjeWx+fCg4o>^PSnqL)Se@cM`HKxT-40LbMk^wb*qvx_sE$pA#zh%D zRuKv$9HkO<&2Sg>@ViXUFIgIuU}n;r^^ooSrR$)UhDQF_eSf*Q^-z8L{m^V1?)eOi ztlo?Oxv%45KP$F3Ul}Q)_n7w?t5%^fdFdd?n^?7TeYZdeaA?Q9lPKsoSkG%HSEMi? zkQa~C&LsKTLuL!en8+93^?8R1QY>Q9fL+9hC-vjZp<3gXD`}bEZuOO(0Nv5ddx41v zP(&}+XT~v;XQ^)E$5aBfQ) zXou}<4;Y`89E32*hR~)xd`M~=%VofyIr2s{F<62W#h-gr2!KFg#qO}=Y%_t2Tpu}d zTa1M9SDn34ZdZnH&H$=FpZ+5Hx5gR&1Ua8v4WKa5xmxkQC&5<08xNGC5Wfnk7RjG` zZl{Q5jHtSk@6nv;e2nY&vv-e*4Uk17edqt6e7B$#fcaZo^S>FazX1QKu1nuQ*~{>!fe6GFHAdlw^7qbn?&mVO$-TwCKK*3Xy`zV4_RKO0b7dfZbTg z2OX>}|NZL;NZE-f4?^h#Y0rGkuCGKsha5kME8TwLek+gcjARKtO|~zrHi(xK zz27)XXanx#vm}FlnC0gew=d)VJy2h+4cDQ)Rw5bQ=3ZiZasK~??^jgtBKsUu+Y7X_ z>C-b0*xoe;!&a_Km(H@a|3x__ol=fnOYI-elntHU+B%y=W0=A9>t{(V{+tjJ;q$m4 zV19varM!0LN%+H;g*rn2_EXrjxUmD*?RxH zffv~l5~`}I%d}MKlE0?vzuy@Nt_kJEhhK2d{xL-o5&#W(u{KXhb@owvO&Dg>8su{( zIP0%(Q}Nfgxp6c7zpUziepfB8%Tk=@$;e)9xm-is4HGv6~`Qu!xaePSU z%)f@zdzeGQ@58W%2Nm6!M@%0V2MEb`bs8_9`I0}r>3e(a0cv@o=RWtpKK#F}W|DQX zKmNFQar6KAlYlk%_y5;VV)zk7#RAU`9r+20nMCD{ri%w|Gm|N1FGXZE^r^D=%)VW;Z(QId`SrSCL#F0RipiOlD^@9_42dvky*fR-~QbR^Z-nVAX)8i>)U=i zSQqfYX^uw$zj@}3eCiL9doz#M|Emb$*F8(nl7KnCqHE$8@Xx^G*W15)&5IEnF8^re+!}@K2x~;*MkJg-=l5^F;c`Eoq4oU!Gye`|J_vm_B9fJ z{L@qNXZK60yJvo`UOi`cs7recSY*?k#C^!T(2@LX?21!#`?cv?7mUw>^qy{{0aCbx zr|ZVwkF7UP1=hoG6UlGgB?65}y&3!Ftb^qJ><$UZrIxFUVZ3=^_MgJc&53Axy>9(p zgY>&c;Iqb`Gg%&ju-2t!pMlFg?g%ThA5&|7e`7A@b{`RM>Ye=5TSZCDn{b8L<8Y7( zv;ih_bs?Pd*(cRvvkpsxvGSuE#w`wj`lWQU+uaOAu@%Bjjw*Dk9A@>i=Elzu6s?ka zKZrJJtzxET&&X^6LJ(7mg;Gw(cmj&=1CXhyhkzK($z=OG%bmEB^UR`Rz}%;-0#L_Inx! z+e=&*uFytV4diyDdLNydQ3j{(&!-ojmX@5BvKgB+cKZfKnr!@J;!kZeXjDDi+nB{5 zv8|i49kT@jdCh))lgbC8D8_jdcv2aKe8tNQ#apzF(Lk&Ae%;^UUusOVZG zo3@sUz{wdhMtq${OH8zN*Vk#MQ((ueKnq_Hl>4-B84yb&DkUkr_Jf_A2Y?aZ2k;|u zpu*!|aUie4Bc)zAxa)peNx_U8L?N`mI6ygQb*Jldx-0QS;j}RNEXQPH9aES@$IiF6SxhY=z|X@gtbySsf^&vS z_bP=Az}FY9@VIQ6td=$eo+5<+*d5hCcO{ps8MnNT9VwX`UN}Ay7s#wA-XD890c-GM zrGx0-aAPlW-x*;kNB*rwi4asUdp>7#x~fj~AeMdh4xLeweakYBf<(9gLHY28>%8V* zx;I`!7^o4QSe*udS@~KJ1xI+n^YW1e0L1(1jLPxcCZgqi4E$hj151_2C|0%B>HQnA z#KMq!v%?M}D@RA^-nU!}Pb+sOPxkbVk9Oa2@WhB4h*?h5!cE$vdQgf;CFGN_W4sH? zSRl0kHupozPKJh5mY{ol(jaVc!2V8)Qup8`pyn^PeO}Wc$^vTen2no*uki`}Z<$CG zKH;s{d-+e&nbXiuf!xyN1U6u@9`<1q*!OnO!)z!phGD=whc#0nI%;uXvLgG{La#VC zWciskK)bD>-|;z2?+<~}z;?AFyQF)eu=#EyDgn=0cJ1O${A{{N?5e_TV1*m~Mn#NO zDW3Nt-{S(nzB4n9NQW*JC(mg0DX+xA;I=iV1qeaeCXr2 zuxqH&{g&goF*w-kaLCu4^c?0|Z-$Bdw0-S<=^5=AF+6O@cshyn{bn>CKf6}aqgbAW$^b$=dgqPT>-f85ds z>)u@2C^*AoEXK%;yw@wO@;ZCQ>9J7SbUpsFX+2>8&>*>fY@!F|t6`uq-)mO!4QbDX z3DF1VEGBBt12qGA(Y8>DX~%VK%QsI-E!X|~fL`rZ0;0ME-4!p9NZ30aiLB*rnLjAX zMutVR!MK~4FFh)u1ytVNCsfx+Xub6yqlcxi+fKg6?veRGt_HWw-XGw@23=8Rr^cgZ z2pzc4timZ}QJ*lfj}%*CN|y=U(5bKTGXj}#7+`)Lp)kHXfwNirus#knbOuA>tnaLWL19un^4=sNU|M3LU~@U`7ih zI!PO7>Jc|IJ$c-gIQeqcL!0E4&=p~6gHuf~Fm!KsTdSY08dXjahITpA-G7mx7b3bH%(-c~zEWOS1vntYb?%1ZMIC@@mnzwG}pjXid&`yq_up`&4 zVirLx`j>6Y|FO$6%z*$f+VCct%t9i}jI=kFp9K>JtaR8o%K?ThaaZf&PIqcR^)~hJ zRCIuj2%x`#x~mB?G(`-vUFbE1e4eo9YS6teO+gd`6q3X))!piw3oIzpg@{L+a-pl6 zz&6lkQd~(h9&8F#V?Zd;ywl?;XHP(?k^ihoPqW&I6?mw9%VwTuaVehR4?@1#cQoBw zzsW46a-Qa%GoYdtMoMDMp-|V}*ht$?suB`j{Y3^^F&ujAKp(?J-W*z{?Z^8u%d}&V zUp>g~X`!z6t}d@s1V3LdWBYNX5?n4}bbuAg?tqhKv1N+ zV+iT)hT+_^_jbSg`##UB-}!a^Av)vp+<9GVt!rJGT6sY&(R{h;)^M%iQwpbTR- zT6w~^yx$H8{1oRvHQ*>p za@Jz3Uj6$Il}^^fr{bk!1wH_ozmJn+t7Ph6Hezji za4`^*1LuiQTjMe5nYPILz$U!78{qdLnF`(dwW)%SM^PdgOQVl zJMg@UCMM_{Cncs~0LI~@ta2mgu7m-};K&sb;Q1kP&h6oI`_J#$08<{eG>8T_Ap@hu zjZ>e1>qT`BD=D*X;e|ju35jXoz%c5Zg&jf(Z{N zhBiB#w)HN%#Sm$yg9SLmaWdJi0zJA&-eo47+q44La*VRy1Lo7falooP4Of$6+MBI| z?Dg;`bCCn4y=vm&jCx4QCywtLOM9d~&`OzV4bk~U_Jy8%L0(s&0lC0yjVPv>fw6c4`*_4|xeEVytcg^V^xNHuEN^ z0e9*&qq0-4;Wr?FOi|=e^#wk>tF{FOMO|qUW?so&9HPuxRU^btx}~Gg+4jrMNz)CF zRE&4B;pTokM(1w`rJmN!t5WJ6GO^E;yZe?e>sci?yszhzyV(LuyMRz;knd`i}yluY&ya5OqH1(zcFg$@7X7jb zR`Y9zs{fL>2xXHKyz5OSGO`+$dP(cn`JCEy?djDPHid=0OsW`*`cG9|*+Np$B_YBx zf@U0_jA`VIm&vZOO+6ZW@GR=O$Qi$4C_jc}Vdv7u9bThE*QoOOTdr(e zrdYA7v}Lb+q=!n5)q9@6^~YXp$AZpJPRVj(xD#@6LEZ$2dgL}gP|2l6tK@8)>?Ard zDQ9m{3|=Obb~~6S<#un%q>Dz~3+N>|94m(p7S{C;D_fQTHDIHK_@PDj%=@xvs1J=@ zXfJl@JqBF|oA=-le~tnTC_vYx8AB@TwcGBF^p#5!%xYyXvy^iEqf$?W^s|&D1aNu{#^x;9df25sZT1^Vbt~aK$OVK@jT== zk-2QK;C&iIVF=;^!;_w@*|?Ql|4Sk*3jFz&u*LP@m7U-fj5)~ZxeQwl(-2RA2c|ul z9?G`70Wj)Ng%|V#J^Vevg2yq5>V?5~!~A=sdFStC$tDV0g?;nA3u8F$?K-*9QkSUx zLKtS^v`N9WD1BpFbXdlqF3GE2U_{hCl|axhg^P4s?%oC;qhjBbs4lb zHVnaBXVx0EOlVdY9gmsU6k^PATuS&d7nPOJ{+hXmhE77ELDPV5 zAf?4<8HnB2ZsPXV3g$nv0&;Mm_(c|;02N{QGuO2UG1VX7<)hF4@R`Vv& z@1YN&wzJ}A&)nh}R>bDzyp}0*4slSNwKaxQ@qJs(Ww^%J)M#IMOjf!opDJE%*<`;w zYS1olSjl`idGT<&KhnP8_W8qfg!n_cc-KNOMuQhGNX0yEv|k=FSXg;b#8Pq;NSf~n z_Tqt&}U_t%O$>4fQ5bQfus=k!j^`fWjgTHXb9BC#x2i{n@#3kw_q*_9DviLp6 zc1nPWKF4{RzdmoWlSeVbN@(QSCMuEXMSY%kj{I~U%O01-NHQIiJW9!}@4=5$e)>Y^Hml^P7W*H|X7K3I9G8pgH!^Q10-fIC;htV3 zwR=UWhmGfL=B?>s6FJY@Qhd8HK`d;KZ2BGmG3nTHGU3%ngK8yDT0yTG5AI6t<8;du zmr^(JPx&~FC902iF`Gldd2eAs++x1aAT76RC+)Qs=m&8}%#uZ;l;el;ogde-a0aPc za#>1fwQP|M1GQuCh5bWVv14d&ro!;8ix)v=`Av!uy!aUe>@ZJHlf3+`*Ximt9=1O~ zh-Gchc0rmmojqw*T}koep^4hv?3yoLBqom6tFsI)J1#D_jxF}*A?HIazkYx23OuFa z<}*Q2LU;Tvrb_FB`J9}V6R)R0(NvtNAn4E>kC`y{K%-3(09&Gz=g#lY5bwstIsH~M zZ`9K=9>oH>(J71^3m!`fR_uqb>;SXFdKhgl#|=*NIs0G#S3SPwq-81VxGTE1c@f@Odu!)hkKJJ8Nmeb#+_bVei1C$xSGbweQ0C~Jax5BP94L$2_GHTn z+~Gcq1#ND$?~1;E?{vf~Gsvt`#`e{9b&t!u0cDoE)|8`;EJZ-G${LSP_-URD2WFvK zzj(3^p1<`z(DrGG(covIG)G-fN0a^3+j~Sa-fgG+v(BUP`GH;#QkvA?@`$07?)9iN zySg~aGyZy`FV%+5fgWDX(L>Ixc5^eoM5K7v?yyZkiMybXbFVA2X6(3U0_zXka+bdh zyFZN`t#aezTPf4SOb}n+D23Uy+UwaCt%45!?Q(~4Sps#2Pi9lKAutpx=Y9WQD zV<*6F7QQkCuqAd#mUNWQ?yjwyS=h{S)t==Eb zqCWHX?G&ikXEm_uTxZZvv2QKuR|g8H<*AR-#n|I`iMh2l!BvezhAyf>N;pXYD0dia zkV}RsD?nTwzmHZ)e4E@sdKAZ@XA&Wpn_|hapXnAEvWzVXf>K zi{ipuZ4xKAA3@%b0C1UukoQ6c0XL61@UukZ;AG?^R|W+lf!OH-`}7WKlcX& zA(WWG_6bTuKSHcp84#*I?1wz|dV&U0#~tx|OYC#0p7xse61wtr&N!4YDd%o|6^4*K z*|54rDY$YbOLpRQS$c9p=iu^i#eUdb&w#7aj|El=%O6gH@e8!A=xH1KgeP5&Aw@Z! zpgZlCIh8BK1rT?(4lA8<3)`Qb?{$DFqxrThK!uB_FN_=R{uwj4NYZZ*W(ewP%d_I<-IUy*nIm=DZ513X_~f^d#rY0*m7%FOZXeTXTTDX869aFEEc<%q`JbB`NuO z5w5dNsB(edc2E zdFs6gjCHfrzV(sc%2rf^+hOf%Y}zDLq2#uY&m(Mn|2eU^eVZexR8-T)YQ1xRFtYNr zvJbj2iq1_t;bMHw*{j_%(DU+h>B{ZYaxRcI!SrV?0Z@L>pf;kp(LTw(iXzy2sJPLv z^`O<~o@syXG~&ZOXI{j24xpv2Sn85k0@bY&wk0>2(`lNL>H@OWdQFvz3n zcQj?)?R`JfuBDAwQmAs>?-&_KhCnif`2=!9eEkHxH+eYwLIsoBwm1bDI-FxzPB%$XL5vl)|kXm8L z;n`@fr%@~Tew9w%=ZV+Oak$R**|VFMy^~TYF%65g`oX|Lit8~lEGI^-`t9sv*jY&U zYlNlKM8aAq1IG|{DQIWx1Z!GP?eZRd+!s9|a*b6V@2oFm5P#bCH zHYMlG@dcg4HTIfm&TNG6gh62uIDG5R;4Q>W( z`>`j~+FGvrsj<#Y4E6&$t>y+PR|ZmKXa~#IZ}EnZGe->@M8MRFtsblYR#B4h4npeB z(pcLs4GUKi^_$_*7sDkXg=cDb zd9Id$4A}et*_{JemA-Nbx{4?AnV`?u51wIqv%9Z_atX6x6>XqQz!2AB>UghP1^}QY zW&;VHRG{d*uW;I=yz~~A;u=1|1+pWvZD%|~ z-2Ab`hQy$ z*yX7I&5}TXotUZ?bNolumAGvb5{%oDm*Zi7yzW9wPvbqyB>%U2&ELNCKL%$iSxBuw zw{GLr?$R%ubN;VW!yx37sAyWm`o|Nqq5ArZtbo=j``_xPe@>bZBW}Fpquu^|yxMW& zwU3wY^2d)Q&mg2tA2zf^^ry?x)0Lct?Nd9&Ys{Zdia2gkdJMIGzS(ax+Nq*bo{6qi zruVb@<29S(oB~yaGzenNGc}x&8C7XT+arfrT_-3VyX8Jkg5U@owTOnevB& zr>C0-s}5H_<{1^dnxq{rGbyqdM77#4kA&sb*S7rx&^^(xgm0tncsB+Ac4CA^32?v- zP|_GL_QQ)op`3cE|M{ubAof8-&_p{~7JxHyA|KQ}`1fw`)ITeplAcZp_5E;LPxYU_ z-f{QzB_`_XU`6Sge0AC^c;59j%8MIc|ub_bR=a__~f=%+kPvw_O?&l*2C8+f_k?4Rvy18Al zngQ7`WKF5X@Cr(pNg>Tvb2aE+(GEB#MZ{?WR~r0#wrBKz&cnaI}G{V?niwtdw}y!HFPo&&_CDa-yx-T}XfC4`djfTm2+w$q$>)&dChBn-^l zl>Ryo8Z;7~gA18MD~X^jmS>UMp|8lWwKRxoi0tWU>Fd9G*Pp*@5cAx!MQ#>9;%#oI z&-#-4^SLe&eR>fLo=ZN+5JDJ8z3F*KqPZ%kH|mfK5M>!hdV;TPKPT%q7tt3@23fEC zkn)5OiVOY@WSm-wcc3>-hf-cu)l**)rWX<*B;AKRLokw(5IK3$xSgM6V0%=w3g>d{c(A3`F!4n?lZ`oz1i|ak^^m8!3JdBVOwNpwM#F z@z$g^*K+4+#UWZsaw-`wr<^t12UGhB^v*MZRw8$zb^o-0z%xXs090tDQ}i-EgPg(< zX7SHYC#sOWsZG4Y+@EE1(F0w}&So_(u`~5iW-ut0Z!%8a2XcM@SK=y0<)rUxkXwO0 z7tJ=I$5eEJ-2ol;^gqYIi6VEoxcvzMuL?4MOHfM z_I=rdSccf{L^RQ9r(U0<>!GK8-g%wAt!zSng^N+r;%M*UazXteDqj`=8`&qD?L)<^ zf=HesqoG*^!4mi5xY9Ehv&}%^a)|^>`G~-Qv2boYHjo?hBBBTMs(BOdXA*ZFG& z;?@%RxAnF7Cf|2s1UzDSn%nr0A9FO(y#2wR#J|aWey&X)Si+0*zFwF>2GUQe3L*5@ zXd%9VfZT;gI-H!j2yy@v4xjg}+6*tTIOGOh(-E0W`B9u^YHy2%hYwc+OM#AJ5S%Vc zz^!S!f~Yyr2ItS7C7ohXV(O%J@A0D^7WCE?@Gg;@##SM*E-O4cKfau{KU_veG3w$N z7MHmRF&n*p^D{p;-mebizkWwAk0-*#mcsZ{ljxjhrAa}RcVg1`K|wesI*nPqv;`E% zmh(*of!4(P8`FB!xQfth7o?@-DB^`(yiB^JU%5u%RR_S_Tof#%J5ngOHaOg^jz=Ef z#YEMaa$_95OOA(CE&JYGzH0&0BL(EmY!;v?@b$EZzb&x(=c=Li$1_3n$&;$*3(F>X zCXQSCFtBaS#xotMC%EogW3941XId{7c`x)k#M_zm*Qwo`HyLy1!&K^(%`9YP>o^zqY1||3u%(|*<7!F&|N22uXaZhK`9a}%8bm`?cyK>2f#K`R>GRyQlVQQ+&u_ zu>Uc%0P~4iOUqKs{lN+nWi>%IpsYl_h%l`fsRVABd(Dn#fDcp2Fuc0eurNAfhVX66T*KT zwPU_Su8^7#>9%{;`_b(zi?d9m1|{)mJFKVOqFS(rEz-kw)v!BV2Kg{Fq<5fT5+^*~ z>24PkVGO%N;K?nnT zka9a%}8+!wot^T|t@yB}g`)6yz2&5giSoUzf8vhRbrB$XPV>;Z^(LF#G*5 z`<~AFAkA2 ztK#pL0#{Rad>%`)b#koj4?m?US0`lVQ`U>6z|z71HR#Do!tUeI1NwnqEm;C&aLHS0 z=WhqwE-a=f(Vp0$Itndg9CQ!b$1luc?b#o+oc(vc_SfV5;-`cVNXoR*+}&z7MP_=9 zhxA2y5SFZjj6Ng1cTcbBn8U)77( z-rBI|19ith4nL|w3MV>PnX`)kFLQT>7F0eMeSJ&pu&IVLH4(e{%CW~9P^(2xS*vy1 zw_N&W#1I5^rl7;e;F+P6|Vg2C(l~O5T(!3EsOFGvTS(MpB-k1 zkdpfMlHQd6oRNZul`Z5l$5%TZ>zMYuA9JgyW*#f5X(4y%XeYvcVR)@s=N zuTga=gRxb^K6a!7jY~zZCPh7tC0OPG4PsMFsf^T*(6slF`n*;E;EGz1Vd;VpVTJKL z#@2>=n40iv7_)jGc02FzXdCSbw4H-hp{G6!+4YZUgL($fOs@|^Aj}6UjmunH7#+)_ zGQHiHBFc$B1PO#LU#2G~^iH8jvw=cs@ZElJ`uKR&B#m!q_5!uki*D!brOR*|AH##0 z*68IKb<{9$c|5j}r_cnXuB~UZwz2{7jhp(bByRPLijTi&DV>4L(NBFeb~#!X%dtt{ zJg(!0kLuzy`+o6~8|fxO^%Hha)7GOnO`m&7cxSJlFyfo2<-6@qKdP9YQ8#amDzUWo zyHbZ^5lXEl?rd7_;^I_biI@X4J~hzmI2;&){t{D8G{OT;0_{X_;9mO0dZU4#m0z!Z zXfasW8~y-h36yrl8`DiQt$z8?p9?mDfGEZ3w12H`0vk?;Az4GidMX9Zx_9*g(R_^e z#2t8~wh;X_YQ_hm1#QVVdbO#Ubub~~+>q03tQjblvI@x2DDTR9q*$%V?=m$Zz~w%2 z{(7x&L22YtF>qBH{Nj0LDbgse@N3jD6x~2X1PyxQg6%}Br1we8y!g!C_Rwog>>tL@k%;c`vg}VH6EHS(@RP7tn9z!@%Q*?Vl ziGQP0;7@ooT)rkpMjvW?Dw+NtIbt=o*ZAF6@w}6E#r&(zhV+zgXKPm6}$7F(I900sl zB4k(Y(376h`PI7q)%+)P5T=0LnB5UeO84f^A|G6YQGnm2D`WWQWu^qAqMpUzmLU7S z`6K2}=jDx>RqLo3bM?e~t zhhKrRbUNNJSu_xc2@J80mLw_GTIEh7=1ex8V6_bx#k5aPB*uTd+ zmJIMf@lv7KEw@MW$)kWrhdIure3UM1i{(=XO1zJwqB^QN09fYI{&MQXHW#&kctUg` z)mjTn_%LJ^4+v}vKq7+~lL22saAE(3OaH$f6BLjUTv&!7MH2;vhAk^vLEXb_d@^1L zc-_KhBs|~Z!x5)Z_awp|{f0T9|A2?X_Iv;m7jpaA(uhraNl_bbv!>HD*)TBh4c!_~ z`R5D*fB}Fheh{KY?b)_ifN8C{PT+V39i#NWpD+>VIOq>pB;a;1V3voTLEUf6Vk!4W zu+HLgGE#j{;8`{dDqfGDJ;Clx=73ZV%m%)eHqcLjT>trr5Xeu>EQU+B>nDlng>Gim zMN^v%j>L#+({pnUlK<`A5(>R+JXD+sysH^!_7Tn@ngokQw)67p1-c@>(?I8^zrZ*< z>atn_w8f9Uy3+nLr4l#|{T$6}hw#t^k?~!&DYGT2Gl(0G{V<39^8j`@euMW6RK6AI z1@re+9V{?;U;zKA3i8mSbw!dtA%*B4;en7e1E}hj31i!VklP#-G8gZTy0n9kVLGn} z6vKg5hli4zw}7s|gZIq823L0PY@7$y>2Ox?#02yMEdU6uI?@ueR5lsZ9C7tN+b7ng zCDQ+PH2l{;CeRaR3_o;RDW9e8Cyvy-wlY-GOm~gT{4*zfbOdF;ydb0Z^Kgbl2!Sfl zY)}f0|0AUf@=ea_(%;W90qJQ^P5aQTKP{w5 z0BCn2Xz6dP_=RZkz>XZRuE=?{Nmhy+tUr(hqZeHe$I z|9tdcpHDPIo*)bx20rhW9w&!m$M?Y!HUrYq2K#<}oIV#Ue-w>8(0ECb1zKmNrl$|% zB2usfOM$B$-`bZm$#^p5CATcZFeSM9A6$yXyj&R`7!8gU#eRN$&yd?=R=NEO4sOtXfmtf)+4EYCRXB-i$0^aCt)nG zA)qESyf1za%d5tM6a18z^{auiDBFCna7aIwds%D;6}&rWbPJhRY2UbXZ(}1euw)EC zXhWb&&`3F`e-~8txLbLgx}fHKC{c%Ods+Pw1qY|rq!h0nd3w&j5Z z0B?ng*11(>_Uc%_6XDq{devM1b%p)UIY>GS60ynJry%D}6LcHM*C9Ob3labzWW87l zmi`Ec$!GESM}4=FRBm7P!IS1&epE5;MKmIz1b0sw({kTW4ZO+IC2+i@Kv7JPs|*fc zzPSYNrEgbdip_G%X)_~`L(+gxKWd%#IRbJ0(ssWV_p%L`Tw9oAQoIcQW$SXYI_pu+si@rDxz1z^ z(Q1MO&_Lz9n*83EsX%82l0V1Zl<(6?ZmxS1Z&x03TMSL*u~IBc?7jk~jY}~$ZH;|4 zKj3Tuo|TSQu>}kW5N~@+H8F$b)9O3SdAz!>_Ku7K1VdPaEX?+Mb$FadSH)|hU!?6Ep`zj^8 zQfIP07zw7nu5wm+cNOMo0XTl5$_w^`5e&x|d^NL6*qwhCUXE}bOwwt?= zY&u@U_0`yIR=$OdWL@SsohVHKa3~H794-}I*sLT3JO;a4)&30St}qf*>#y85NFuYT z6c-rk2O7sE&2uipBpz9a=VMiF7>U93c=w?#UBSqiWeHYPyz>GB09lZ*`^hT9viLXf zCAWB1$|jdW#i(C-rm8d>a!H+z!nvCJDE?qhzvP8@Hb*U+F^P*cTvpNC-HW$I!%Guo zgEdv^A=JiBKH%I^{UM-`E?t5gb#4aVM8bD+v{~e;m2Kdi=x@``-;eJrus43jW!&>T z``N}~K}>|R&aEtl9vTJM8T3(5ucF0^cyM<|qt?3788pJ|WFo~dd~(3X2q-^g>~o*G z43j6>0C z&I9`QuO9n3b|AaKWDZ38U2AT3X^9F$$cS4hs6dex_% z>IBViFtY&NwbS{sm|MYOc!@nQUp6F2=Qq)}JW~Qih8VSzevdTNfL>FbjsP>{z}h?MkXUEfnfU zK@Ne-cs_!ifhLF-Gkz#dL@*%)l2&G}SU5{7KAkV0jgznqdk`T}$3ZDTfRO-;FN?_X zY*yQwh|TZYPv&BvVfQ+X?uPYu@wF+sGF?kHsEz!ZN=Ua{tSUL8^79Da5!`God6 zR2&b8T&2Pw;0B*T(ME&PGMC0Iij11Hg|mnO>poQ@e7eK0kXIQM2&+peAWKPOu~v)~ zK2*4W&#iV?S%ktaIK}k7DkuJRMOEXkzQouhM8>Ocm&M9fib%IQ0!*Pa5H6^itN|tT zL{8bj?-3Q~6q#xLu3u5(DGZYN!)L^n#rb8UAi;_n0CgytSXPaI{jY6=FtwbV#zrd_~M$b>c@%c!HOFYX&0Z+N|$4`|T>1ONZHS2B4_( zhzzuIg?xN}gR?0s&l=z@qqMY#sT=+h8t2CSk7Yby!rvIe8~$y=B3?G=}$y zM#}A#{iJg}@GMkN7d+2BLh&;F9%Z7DB#3#CKkQ^?ELiErcm-h5Yc|hn-ZKw4G$DRAe@4PX2|6 z<)F!sbbA~>2Ci)7wyYkna=YxvxzDRUg5v8zg>9z@MY-9H(JCx;R=~?+OGKu8)Jp5x zSc{Cn{x_dG((qo+%^RM#WxU|O+3FiF6F@b*oQo38T_uG}BCNXJQm4Z~zEt)_!V2ZP zeMKF11 z6)DL3jArJ%`P2%=jf_QFAAqOgr`5u`m%yB{2&GaS7xY2*gjub7G4FS+wRFzf4X0A9 zK=8hqf|dcr!2$>Lrr~X3^|>n*x*#Nx=jNPupJTZO&6lr13|@ zX4HyEKrkVEdMf ztj7DGE^y3_k}QjfPc>`7e+9VADrisKI@x=hDX$2)_p5^jEe7)IExr!fvLk?-nwL>> zT=k{2v^t0V*6>=g7xpRW;huShOAhFRtgqI}5Z2G`*y6;Wy%w_@-IMV2Ot)JKjd~e_ ze%fnUwzWbqeU+y1$=D;x7a4> zcLjQ8J}FObpO7BXV#Tn#P^^5JLUlSsU_KHm+hpdnpFO`S1L8q6tb7sM@$BOMB(AUs zgsr`|cF)-!+jNlzEq5l1`R0dUTNCCXr+r<>y6u!wd5_P{32a9_Axr96MI)u7926Xh z)_+TaW|C!SqMdt4l=C#HiYn4T+|ZAngZkie+TBW_sF~3AMLtBUK%rFVd?oE#Gv`4> z$BXPViolvQws(kAbr2#G9&o2*On%l2dX+Vgzs1?Fp$C$0h$Zbz{;sZOedcC|gw3jm zp%p%~^%}2jM&^NRer83C_Wh9Y%C&rocOA_}cFk!KnFX5pX8g-yI<)v%Pt2quqP?!H zN99IqHk=!)X_^2vaduB5RCI1zNOW)4YK6hPcD*#onX<`NZw?om=y{(bX7{o0*}CGi zw>I(WtFW4A4Gn%GE)WewrA@XjRaQ<{!dFGAcCuF9+IsZl!iLe65fN>wWRYHnvNWa} zQ;nmvBgi1FT|l))5?Dszkt{ z(%-rg(4^uy@6V$Nw(To{ECt?i(ib2Eg>-^TE>jjMTO$-ut!*p>tIv=Z&BtfL!%xqu zh}Bm3d^amiPZmlAO_AMI<1N)j(y8X;Z9WDOHako*ksiH8qV!3YO*qW_gNehaQ~F{b zbEq9@sr)tC0a9nVe!hYz#-a=t>O6*&?IE+gL7lf$Gg3`k>a2-cfr^=Hnx$qs=z)Iw zNlsWiNZ-eNib-;-36Task>?OG=a(H0`9piMtwpivfZ+d>Az z7kHh6*0g(pT}mfMKMFMmIe$iRUvxOQUvd4JPR{dM?olqq>*8eYUO2H-O{e5rpp~LT z9*8^E0n2?6=*tb%d5L>VGZxK@s20lhB|g`SOy-xFu3a{xgbWs&^|K_>Xl8FuN;Nkd zarFt1xyujG$V~z=RLsH%ybk9O4S6ZI>TAw(szUBbk{J1X#fwPX5 zp~mDLJIo;G7(Yi!Dtw_IfF{NTXd?qgOKYp)h?d@R8zZ>A@ED*PTG!NLJN!L;iUajM z@=BZ$@g-kw&n_e&>YQMTre6!NT=xo}K|esw@s&ue-cZn?zx*`(H$rckVEkIoOLnI} z4m0l+s=(@6qBxnpvecfA0PV7)P(w`tX$hZDlHzHZhjH3ON?U}|!JOGE5g(W{628;{ zbmUe^Iao^u1Z&1afFnDaCT*DSNc0wrYY_r~@)1i}aa<{%`_Z00S7=9J&EZp~Z>U?k z7B6PgWTlc?U3iw6^ZdE&7JAl2uVR&6(}Y6A0SOm`h)bc$Aga<q9#f|D(ACsyXXHod<3*DJ(XP#h$cQo*N#? zi9S)9)9Xlhiq%D=BtgU>4`aY#`O?SaC~&Gv$WpMVfJ9=~oSpfmGeEO*0PkE^TT`c~ z)vVukn>)OBRj2UjO`jZrlOr3C-5PNr?6;dFZ#=ZNqGMvoSEyfoe}7#rx^vaneXKGK zVdE|3c8B{LF{jsGGjsFdrg)$--VSO*HELl+`%L)f zwkq~@~L1iMu;Zx+vptY2Wr4`f zQQVEkt-!0mhV9=_`>!=!LRf+a>ZK1%>j5VcaLBHLQP`gA{3@_N^)bVaof6kUiXWg# zk<35ZyPGFKO7odSEq45_%^}xr|8wA>~JCIyVC4=)6?hmip;8I$wRu zWzvYgM@B+FGnc6iFf3iu;0m31-mgr*06J?})g%JAO3cC;&~7_`_9+iGq`%h%94#pj ze0$^G$>FVmp_T52dQ^I#;6zWVt+N;mGY{|FJ>@nc2w^9{pjhM{_;cru zE81&0x(pNC%F8CRB~((8SIKZ#9hK`%sNA-@fTCUK69a3|D#b+?Xv9bF3}U5cPz>+E z`LYRVlhA)0dCCjbEgxb!{>78!2`Q6fqT51Ud3bj^xOaEjJ+R$SYVj$@LN(g3c5|ir z58eCwPRusWOD>xS>np<}ymXjkO&}{w<({kREisT*lkS%>9P^o1CDPi|ci;UH381v0 z^JKn1OdkHm|Im|^AS2*2kfe$%XM%ecT-gl(&xpAFAcaLs@zq=lM|(?$-FrYN`x_tT zp?9Kuv;edkxSNAuIsh%rf_G-3;X{BhFbDJZkV5&jD2AWmD1{~7`o;yn@{czq6$fbP zEI_B+BRW5X64dIl70{=wX^p@RiHZ0fW2Prb9mtPO{SPgb>@NN z2u2%xbv8-Tn7BDBFpjo@YPoFuW)sy^6V>M%%N(11JzPqHuN>%wnVSf~y?^Uu1+0pC z>Xh@X>v5V}z>VAc2DU|V)UOIRMrVe|o?wqB?X}Xx*$|IyyQR1XOZqj38U)5n@xyNjuP#Nys~)1^7r0{E?fPM}2okcC-H-`?;u<|^{l_CB zlKE=hF;L7)GS|wqV=CK@r=!MQV-v@jwa+_jfFABTDl$tUJt&Zxe+WwIk9>yj-^RE4 zWY=lCS9#gk#_*~;Uz!g2gAGmZN`d9U(6sM*_p?%uwN zG177AQ@qYHV0}=LY2kxR^q@?^Kgl@j(D2*9cq->LipT{(+)4I0>`xU`0wkrGJcoMO zuZ}LuC<{%4>m`_ye1qa>>@{LMBdajx5x(9HZp$S^-i7Pz-=L;)(6_+TNyK6Ypk?+? z(=z<_pKO3jVs?%IU^Nsa?tAy$_weD4`x#+ZuMy_m8pQ%MU>eo6!If&Zd9>MXbc1{& ze7ou^nO^05`977sd{|bDA{AkU{;bOO&6}XSKbO5943sh8mkVHS+r8K0QbBXe=$N8m zNG(S+1`t1*Pba~k zIar_>P861LA_^}yAT$8;SOEeV=FN#FenK3cSV*^Y+rz3p7bZBqt(n?i*F;l$TK zAkJ&59vKK+e_HLLBL>-PoB%zM0<)^YuX%YL&rOb_=vw1Er}#`{>knCL3ww81vj(3h zURk0QGN^!L9gvoOQBS~EW~}Rrf-?qL7NDH1(kaa|{V8J~UVUue1E~Wng-jgZNh7 z4Sm3lVL`{mpeXX2odF}-3N!%H9{&c0A!_=^!W}8rq+VvV{#+RI(T9gc#5X;T)~}oUT7sA!uL2EZ0pPyR(;4t&M=Y{}_1FX2=sIZjl!<~+=>~K{?DT!uq`vn<=k<{y zBN-xJ0!1{vMh!jPu6GbptJ=L36e$qTIPvX{*d*^Hno^wXw|gX|8F1c-0O$Pusmz4% zwp9=l+U~9cp;F;crQiu*owwg|=7S6bH(1uzfmhrNKk~$lsBrc6vvUWPCs;h|g?TZD zj=emdIU7(T885)E9)^koHBKkHu*41(=A*+jBXU3YGe5lmpobD^3~Qe$dcXXjdIqvk zpw$qg62H^WX6kcR6hIhPN_qf#U^(Wt3+pLAtk{gd20?(h=9L%SS+1<^k<~X1_0wA_`KpmCR{R%4@u9e#I zM|XSM)%I>!g0yR{w(iIhY=xdJ(BpRCZt#;UoXk&>5}E;auv#lJ;Z*Q_o1I-H^C`?fGb<-s(|FOR z5Ra-R&Hv^9q3kQ5qHNc-6#*$}1VunVLZp#~0SN&~3F+<_x>Jw_MMS!h?(UFo5NU?) z?#}<2@9h2UbN>D9v-eqRmdiB`_|DAx+;QF4b=}}ZYg@~E6MYaDC3szmCIo~?Fhyx9 zjl|XDyX&m29;&hK^{0Myn69Uj#ydbY)yk??b+l)a}O{He(M*19qWBxY4&|&xFY9F9!P~vP3lOKBS>?AJ8@%Lf?S?ZTZdlP zrZyIziKf+TMqj%ctzjDkcY#!Yv06ok=a(l@VKPLqS{fuJ)y@{*WwU(Bno}QDxO252&=YP((_+yY3=}daRQTT{+^dozq0ZlFslH=?8jR1`n4ptG^1C(>Bh{{a7u(5 z0QTrrM8(JnZb&`%n;*Vkh?uXvI}ofh24iOKbWo0shiv{pIP-7D8}y z(1JNpLiA|y^~o6}(TfjGk(X}RyQQpSmH2XFb75dyb6(~EJ>mRWSBdEF{`B9OK8b4x zGReqtE%?SH1FmDuF0k}kDD7pmn_cror-~>ACv$1*Zk%a{}Wa793j)8yn^66B@5T%bzRr{B$xyh zro$Z#7=zmyz*o3Ad-IvvhqIn;nq&(s{^PGVpkB$e{uvb%o zeFO$b8E8%0%FJ&&vkO1gdzfg>5W6U2qy+ob9;V*se{>$Z73;y^(9$Wbygr5}k z4+-mpm=xYYu=)A>NPFXh@hqW2YXDXiC@E`t?4~k>AZVXy>CeuZ?)d5Y(tZXCC0&vP zjbaL1TPycdb6J71h4FdSvcei$LhP zgrZm0?bh}>ByUKE>^>We@8SGkdr;7b8qlRMiXV5&z(9R9jV{ojadMk{8)PA-M5eex zuJTfp$>PLT&N~Hgyio9~(Zh{*E5EFM2;qQ%(@(lSR|+iyxTW`b*niCRF9-bF>dxGs;aZ5sql%(y z@J{^=o&(-0yC$Ae(E;7Wci=@ne3fN7oLLBCcJKy6j_(;;MZY4(ftYK(Hf5-qPKP!I zo+~lNCA@P0S&L72y$dFz1;_*Ga$j4Jny=n=?j?Sr(^&`ix|@@~uw0dqEAkh}LlC*7!NktAdRmtA61=N1tFy;^L9!rLJG< ze`V4A2F_#O7fhQko{sJ12(jsReqnd$j|q+fVWJ5OxFMS=(u&QLY%EpiD6w0qmCm8L zZ|oJ++9=X?nWI&?&b&5q=s$5%pK06R6qxv3j}Nt`X#E$}&!1q!*DHk0;I}hWAb0k= zo@_=cs)&mzR9R{gzc!KU!~TTuIF}X&6MaK8jZQ9`;!T>gk_sHm0s>17f2HFVd4fkH zD^kqI3k=!My%ETfN;ar&pj9(QXcm6zM!Knmnsrn2&7a`F)WWFVm+@Ss-dWIu$7lw* zyfS#qq2NI2_fS!nwk-2kFOZTjXjQ4!zSSB{ns-zr7a6CGEmpI>Nx;a1@%lO9#{fyv zGLT)GO_pYCMbVE{gjA3?JW2lr5M6~B#_3}Ot%Y1L^4k%POgv75WHBfWTF8X4@or^p zsu`_Kpk4ByEj@FZwwdISnu!K=s;lBzc=Mx|wD?T)iWO!`$~EGnm8E7q-`1@HYMG^| z&(m_bTu&7%Yy>;7cOlS-%C57*%04-`td% z(cQ?fDwWpEGZ6@~zn=kxx6u9=PxL@p>{MrHOi6xq=(W#TrOk56nJ}A-IjXu7v z=l3H5z|>AHZEvo5&wec$bM9t>P+=}3VG;1tY;)k{ay>Q~E>`3E88^NVrzl`PdsEbO zQWAW!TrQ;gOO0TRS|(xLbJz@^eF+41D&y~zFe+r~o0i%sa^{X>%n4J10Lw4rdvLsN z(?EiuAtlw>_jG>12&Lfk97VAI9z@oY>{VENakQom!gAUzv*}b{7k8!E=(idx=GSw9z!U zh_e~pp)TDrYh*K;ppDVl`hJDN;nk1A4~i9Ul~nPA<)f9+-sW6N?o3rI z6=>;X4JPuAJE&$HT>?sh2G!Gs5XB5MzXC-Zx~jNbR_R2Z(Z@_$+YlfFvG%kb7Sl{x z8Ie<>21L5W+LyyODdri_TsO8EJl9yQU$erST5*7Im=jN_bp77R4d_^%&LY34zbVge z`4FV_0~B!8jK1Ye4p~{b@^C81_#2L_1l!=8r(#-_iu`3RE|>?Ei9O~|_J~e9Hi9hx zklS~Iym2c>@chP+=IK|3QO+C4AV{Nk9`Bz^kdyP@^?aaZ{zg0)e!2qqaatA9e#*!3 zIo8_MIg1J7GbyV%A*}r4IVL%tV;okq7EO1zb^Y<2gPL{D)HOD{wV1$zHx^zMHmyz- zI`yAaNj4d)Y91jF6RDLML0@=Gk3-@wYhIZWy;b1JQ|cYw271IV-#2+U`050|2wZI+ zanOHz24B17Q}rcs z=dXZQd?|0mYbzwze!&$~^+}-M5_C`?fyBNEH7luy- z_mS1xeEo$K7n z7k5>CY4jg1ko!u8klT`BW;kgIdQdSCt!JJajrFTJp28gpX~WA#?Ywr9oQ_L%Ti=R6 z&%wnO8}(yWJPn78jTNdJA9*GlM^Jm20i@Tiw}I_gQ~hzxBytmLS0rkvTFt%C>*HbB z)6KlQgRpe@^nA^{F$O4D+w!v5kwx^rJ@;bmW79{r-GZ$g6&i_>AAB&Q`)y6sN z-0F`>vRRArjC4cdgj0Bk#TkX72sYDBB8=yXke@wD*ki z=)NE}_(%Wwzy7TY>HdlT2;^JSzN_RV==e0PvjMjr)#?TqK$qiCrlGgn1M1|$i7GQY z!cb1+$Tr)mRurSASs348wm^|+$m%KZVTF6~-k3If(<$Xnv|^o=0F!04#9D`f*2$eu zW#}XOz$0RvY!INH@c@2~H8nLI%T!Qo8l0+6FQJg=R-!T?SX zwgc!^j$i_KIlTDjdcPHG?1vZ@+&h3UM=38V(f+ptTqGl$@)Oa2xBkcySX)IXyeBj$ z!t|W%`TjB@DOw0T5S{=_uG;&m(KZ7Js?rszo7T!9Z^o_-9H*~syuAHrjG&v8H zeU(6SmHs728NMk12&nTP5uawj10}FO*W8-*(BB&R5#S|gN(^nQoXS+NX<968C*M1fUkTq2rrDUIW*j?0@3g7tmSVN&(;vhnQqt;kIm zwGQjWYVXy+6j<@<>b)2Yco4@`HUjV%!1fwYAZubM18dc=pmR5v`>->Zl{WA0g#Px= zPneZJkDV$tN#aBV7e*M;QTzscd>}}7QH-@c@IRIDJ)U*5x{^Mq;g~VN0}yxTQkv%t z5Y;$3kV5%PB(_R6s2vCSop?p(NI7-^KYJIawdq|@DyS({c~?e;Z%#EeUT3OXHnOPt zd3gZ}%j8e7hZiRyOtn8TxtHe^fg!lSFTm8w_PV?M>a-22?5eCpY}(4IIoIJ2>d?`V zOd#TDwX9j;i0Ab3G*eeG1$N?^8)+is&%OnHL<#))`^87O_1HBn+EL#XrPr@rrMjI& z#z%RhYPA}0?&Dzp<-=P8RnadD3@*4&kC>WN0eL9jG~#%Y))rdS1OKIeJQNdtro8#I z4Z2k(yXTVt2mak`=wk+k4mV(!!~trWt5GCb31ff~6q2|shi^EV!hjnyaRW9*VumT; zCrfW0tDkJDzAnLTiTi{JX7j0%NJ@x3judCnS(LmmAPS%+qTmQJEr){&XMw~kOnYJvSOrs z{4Qw;w9qA6h1wKSX~`9qGd+P^zBs5GK&t`Pv&Bt<+c<(xV^35NQ6%LZN;)38&EoUt z^69B`HII0L{`O}7>n;EFpMvZZ#bD%fl`o9q30&#FnoR;c$^zbEM^42U`QaO7B|65n z37JkPMoU*jSE`_3YkQT6FqeZP{MfHwDtdPNt1y;qke|4R!?|vNT9KBznt7Hj@g~Iz z-^H?m|1Tr3y8>>^arxSWTvVkLM_Ayx%W8AfPLgu!*X7Ju6{nWhaFX+(1Tm!k#ppN? z;aQ52H~?oME6r#+xV`#*=d}AU1Bc@@S5qUn&MBk#$aHe`ZqbSE_Kq(kTZJpd-&Fz? zb-}DIe9W$w0eXAwa(a6u3`dFWUp95D%4@#wxn2vR`9r@yEW=y7E_IVHvD5+aPzO7t zJZ`{@feA(xt+hKI^uqquWlaqX#@uM3vru53mo#s>wP z2s~ieQhk-6$Nv&-Li_cXJp74&^Ux8cEC%<}<=g?-d?F7ARtKV+ZZEfBa8@M{QaTQF zX6uaNWow>f(YtkUA;=#NL=R7!h zFJqQ~t3#E=Kms@LoynTy-NQH)ijiZ~vMe9+2M#M|E(9bnEZ%Kw00J~X>voVQn3g(W z?pe9O(nyktNUa8{4ug(sUN125-3h&89x1ZY-hx%hYCvnYoTgo(Z*H&fwJlFZ>zr+* zf!Ia96KE4AU7@4H6@~%;B1FyEiEOY1AKXN00O>a#>AhLlr9+!qR-$KSD!s^8$JUea zDNIH~_2Ld04g2FEuPWfUJi)z|w0B54P)VGaz4@l|12sIrDv74>mB^U{hzNW8M*aJM z`yAobQ*ZFe4I^VdGl3VPa0NvfylU~h+@MK*Ph0#vPY$R!f$7C;WiMl$I4%B9#UsQ1 z&0i4PX(aV!`E&v^6s08Vp-(=V7lXZ$L4GZ#ptzZuahlO(P&BhD!;rrUJ-*}fk34G$Wv#Gx+TR)( z9;3I@xDQoC+v`tgJ5Hy5SD|IB0_>pkKw%5Fvl*w)UBayJ_?3-@5ZfkHw^v(s@a}0E z`IqoNBh+6X*Z+!NKZC_}0FYYwcVTbz#)dgu$mKqgy3iT`12Q8sIH;6p)A0lsok2M9 z$7!-mN?G~vw}PtO+SB@wmQSDW4Bea0sik6cz%3YG6kxu!@jK9vRYW-cWL=l3=}QEzxioRmyic zGU70NXCo?kz}<3LyJhhB^to;D&W|v=x~vEq*)LebweWa~pD0+Um--wHjUx5e*L}z~ z7OVpQFZ0kpKk024B)F47qKTqXqGJNj*L^X27T{zp_lIY{X||QHV~0+a%klkqp7o)> zFYS5SYo#c5vk_V)2bZB}5bF(xGYO)`%uf@(1q$Ru7^&nerI8r2QjjBUfpS_>vQpnU z-eD4KJIueTP=E&`H1e-`Dzpidfj&Osv=&%j1!vj3Z6Ep;6;$C-y!a0(zwude+eJsfF2xt$l)_HS22StJcnzn)p z!ck%KvJlSBbU2k@L>N31xYi-zwHJqmD8p%Iz=2J-{$&NJ*qboClGW(-?>qYR6 zi+n{R9i*Nbln1FBh(I_#}@}>S=O8I-vi6TT`&+GLoHyxg0te>w}wDS);A3KHZ2?AI{ z#Z0|kXe32HjJMgc=EM;$W+HLhKFAOee)z1~EBE|_bK2km8Oe87vJco@+f{S-IgD+d zGWv4cxR%RVnlfQ=pQk9|0i<8s@#chc3Mh%?Vb!-~gWTi5Q2@xeyCX647=qrt1_|2j zpgsB>K`byCA@Mk7@!!vuqf}D_3wu1+;T607bVICwmuIXQ;JQP257HGZ;-Oa)fOev7 z)WiV~c7iOZZ8+Yx1n7Q8ooVF0V#bN7U8dfj|D#+0LP{^Gw^{f%dYvoS+^v~*?ROO{ zhA(MHJ6++GuwAza9`ho@8I87-NM%d&xtevZWcTZ!&vz%|)-l?J8=gOBtXm(+!%k<- zWrdlXx0=3bsL` z0Jn>)TN!F1bC4FJpINFlC+pitiwRRD*WS!+W?0HxH`qBh85c|w;a*)}&k z82af^?Iftnp+$|CaW~h!OrF}QUSM+v6wubgLU0|5rIR5uv+j1qJGEQd1y*yATT;+L zx6HbnY5tg*NR~+-$s3RIOZw3H$L;e00mXx~es3V(uyjY{HGasK(#TQBe|p@r2N2CD znR8^|()Iq0W+Ao=?Xa0}=~#`1hM+5yk44&z!-&T5l)OO1Ds z3Vto_qkJ|Ca5o&$gA}nYAM+^Gi8w7y(`mAOx9-kc9(Lq1481woNqx9i^$Cl6#AGYb zs_CT2KoihE$^jK=$n|tPb~@wiKdBY|`)eWvQ>M~igcKD3CJAH1n51LdLck<|rgVX_ zCD`f8!B++}C5ikuBY2Kr|8O)8y59rFrfqrNlkX7)O1)9K3T6@wBi<_6V9$Nd&Zxr* zV2G9G71p-^Y7kV|)Ab)%Fu?k=uv;BCfKAG#Cr`jzEMl&GZBxJZ>Q{K+;oAc4s&%frrFTl}xtX9RMSm@m8Pq{+n|0?`Qwdm*x5kenvund>t$E zRe?QgaEBgvjo56oH&$3_evbogBI(t$%rgb_u4?xl4fx!X}p1vkY)pREDWPvz3d)!&{xdKMtwn>S{y$s*5jAwqS3G zq3hZgS|*7+4&eAn-@KaRiCVQmfs%S%3xLXf#sUVdVv+yau17v~ItJTwF2UXcwxqh@ zV*nvD1{MxSMde6zx=WjRPApn~6lZ_kU;b07{3AtWNT!^EBHc9o?3y>vMDg09CYnljXx=VNLoGkLCrKHf2I77 z%0on_{e>LC#fq|2X|35`f5R#^DF580cf7*X(9HQq*Q$u)9j9bZnwTkw09uqet=lHT zeC0ug;mogjnsre-<-^(uD(jv>erN^-t^fVD`1Ahwmw(Z5p_X%VX{=t^nVPDyvU>0k zVgc;DOTl8h3C=l;Dx@Pql{&{)>S3huYJiXtT_rho^1l`2|5cX%&p+{FKOzc?BC8RM zhFe-h?9SZe(G6tT{a=3zy#V6(Zx$Cd(Y3gL{XzaOHz{qjU_TH!d{-Ao`j;QX-%rv% zzaR<=A(*+`eOgS@?;qdDe|Y)JB7#1t3CuZP|Ia^3Plot{NDZ?4qfqD{$NZl!?k}IQ zrFh;fCljM6{(t^S@Lq8-7|6hCIP(AVf#k;dszXa7Mfc+W{FA*8@1y!hgRPy?Hv*3CDjwl{q$BE9Yi-t z!z`div~{^9WKdI-R#UGxq6dO7KAuSJ;bgB5x7<-DZ1s2j7R1)4vbPRbb_%^31DZ_N z;Zt|J%4NOVOjfNnRZ`+40?@I;v(RX#o9>&%@E2+StseNA_!HEt+p;(M$v)qECwP>c zUl#Nw3p5ECYel)o6Vx&*R~ru|y`0k(4Pz*}2I`N`%A!yJ&&pJo0??g+Ut| zXT;#k=pzH3%8$LnsiG8ep>9Wj0Y!6?Fz0^MFKQN>+Z`zgy|u{!n0w)hlFZ+4J{|5C zg5{Ajjxe~Dm-6Z+pcbkb#d8GyEEN~LaaXN3AFb+#n|V$wDC=r1k}1>GU%L$1tFU}!FETNO!;{SOd1xNADLA|jJ8l4J& z99)e9nw&AvgOva+wF+E7X23_iyx~-x+r$|=z@^`T0jEJVrBFn9Zccz$yMcpt@YJ|R!Wu;_0`YkMAuRho|v%+~`D1dg#47)iHR zcHwNWs^`lk4 zxPn&=kaKyxR8SYdfUd$4kR2Q0vq@e~x+Wx^Pbi9z`zJ>|#`S3l_p=D~Qo(xEKitrO ztG3E&Vi4TI#L3bQi_CSM9M!+nesy$eq*6>fc~$cI@=6HSpiH@TJ!mUfZhl%XuoHV` zG*N#+mFkN*(VnVukDENPTs*1u0*K)2MPmEPqH&NgF zH-fF0cY65_pwgWZCDb$k)W;%!^ON}KP>{K2&zXd#(<09iEmkwkMqIt@a zB2ZP^uoSF`>i{g)5y;YDfE(BQ%-h*_98S>y=iITA#AyQT%T>rs-~d?y?SPvh3{t(a zHV{XMNRhB|0-WD2_S#l9E%t3~q?0z%Xcpf0^uNr8{_ZFq($CHtHIMc_R(LX}9x$yFGzSZ1GiF zRLL5@W$-IL z=Ovk>EZC+E11f|9pb=_ZKzR?w4cNjW`Xo{bHjeL@ivA-QIFC0WC)i5sfS6d- za5f8rlVA*&7DsyOQScY-dfFeJ0UdajyY^awos1TAcsb3nEPQ^N!)cySi`5FA?Bj1d`&2dDN3UWJWAv9SSHI!?FaO4EEQ!Sk$ zHRyF6W%*AO)u9!m`Hnk=UMiyn>YP{WKf>zS@UwF0*}PpH%qBR9Ul&IUBy6BJT(r}x zSLs{6+?c&~cg$Vf0ZKwTR-Bw}*TV6in{}7O?7yay@M#PFN7@!-r`(5g>mlsy{fnIz z9sVK;5>sI0_G$%UEJ|P@(eT*OiqnNoUH>G;-GOljLPnH_{+Mt9*BHR`TDE+)RoISPNcGxKo z8!Rp;<+2A&U5=U=WSxLU3+ohTGRAizz55&ORPQi*AGh5N(#MFuPlgM#xQ zEf~|$4TG7?)|Hz)eOoHfXjK3RXc|1jt33?FfgU4;7^7xKwt^{ti+Y%rrkl9VNmP&D zEvQf{zlPex)M0>wwKkFqBAIx2_#Ma5R)#o4I$^N2 zqEG>^szoxdUf$;=$==Kt+s1t{;S&*8Fwmt<0kwd_LXn+7>ESy@mExavIr(4vMbOg1 ztdn2ahAov!)+Kiy6LLr1ze#LFtlQM-Lv9U{fT5M>oel=!(OkM|iMw%Cs+=stFq1-X zDBlv5NgY*y+|(*?2I}RWs^Bz|=)I$uocn&Bg)%YB&0dA_vWCPtZQ?KerDGd^HJ)2q z)?~S#}HD)g(*H*(VSok3}Qye?5VH_v7G}3|!YJ3=i8qh#;Jh#Uq^XX}@8JUOId!Ja=dM+b03hZeuf< zJIfcAU*k7(rR}9H$H+O@9(mPXGE`q5?FMKd2K3Rm?fjmc#f_|CJAsaOM3r7dP{^e`q0)Odm*!Y zi?^FLX>*8fTLo1uX0yGUt9Q-G63Nd$yOdyPljO5du)zJ*ASW)FmqGSnUtftpJ%Q_J zg8-ev#j@ii`5Jw?HeqYn$0F3IbMZrV_o;Mi^zhijf~@+b^#FHIpm=H&@*K54Ih0!X zQLsJu$$m{MsdCC=rQdc2;TdOzDZwSiy0LXv*-XCv7Q@dg#gue-;E4ybDC=)g z7bu1SLk0Q0;Q|etb^=HO>(wGzF`Ut@Pv!``>!P2UTzwu&1`5v+%f<_^H^3t1K%eVv zl9O!!Xn!w0Xk{BA8~PG>0((4#2by2BK~C|2TmWvRh;gr1q}Q|9C#OIn2LnYxeTvVs zhCXYqz4{~gkVf^@+Br>JF0a8rG_3{qlPsZ zqCy)er85FN5_y3Z;MFs7gE0QnVnBTuO6Gigxm`XilasKRPzFG_C$J6&7PfV`bj_DS z1q`sZ!JMcL&Y#Y4W&I#GfEgnI;t=G8JrNOr>AC=jL$a3u1M8oL$K>)o;Rx`>uZ4tk zZ~^{!l*4yJz6z_gESDWkcQ;l|q<2ps?$vt_k0*bklMe$3$rOa`NI03}18_|`|2o2> zTW(XUv%;@_ERiI6`4B@N-D2Wx_RihySrbI+H6|KzS>Ly5kX)98do-ESy$&t-LE622 zra^fYyS03D{~_iWun>Wn{P^rHZf!f8Ri-x@QMl`@-Ur^oRkD?Oy+A&j9N66*a8u#8 zrK1gGus015z{~CNFaoZEWz|chYa2>9CA3pmzS!Mo7n9Z=Bi53uaIi9(b& zih!c40q*9R(fA2Pf54bnX0|70rAKUG%UX%YV+cUccIiC&;ZvL#K># zG3t`KY6>%&B4c}e$F#!JLC0V|sg^fd(P~?d+{WnCbROVwbi)^4Tb;D}*?bZhpE+(b z_D`Q(Cp-R5#Jh>ynJo+78K?2MM7wOG+$9aWbn$!_121jN_k5vl(3vh|i>Y7y>V6L{ zXEZr?Zd|AN;<+kB*4649AE^&!tnk{LtyY?CZ3}TdGbnQB>rHZX^1tD;DG{ErQLhnY zfE7#f^Y)y~*}uHNCLnRt3=*~>={!sOq&C%XcNXx-ad6aN&ApYBKPwRsnQG_ezh9lH zvu#7qLI<4aOD<2$)S@G|CP=^Ze%RvNhaHRky+MJGus`lmst{cToW(`~Ytq9;GV0F@ zvU)+Xl!@e!^YNzJ6%Tqi>yOFjE)`Uq@Gj-DM2#_A_kevqPnWN!`X$j)7pTISC!!0Muy6-F{~PUij_C|LCFdEL#0e}5Fjo3Hs4S!eD0v4%W2FBg1B z0LNFCeaki;S}AeC9)R)WTZKhs;%M_7UR7A5U-m=trx4LL9-s%VfbS67N5a=Tf9tal z@pDc!si6L_XSRsY_pOK>-CoTl2r3@QaA-;%fjB9Mp>m?-D*S-NuuSh9$C+be>%3(D zO2?b(b{jAZI99BJV7OOW`_a3gx|{73C0nxdUb_a0f%18~#Yr!qXL%$O8@dMHNqh>o z?zMzDFMXCI5Pb$OxrrL7t}EYs4AemNrNwE4!j>;`C#pWUikDS+2rF8{&@oT6pcB!@0Uk$ar1fNJP>#sZEf7ndtsWQ z9K5p8tWhzcyh*yf1`dNRb96WCQbbyYkxCZ53{~ZzVt>B0N0Lf95S!P;F}L^Gx=`p+ zb$hLDGdC~aYOc}L2Clz=1QRt0Acd1HN_pbdtfz(iCXKMvXK8%39(Mh>-B|n_ilqHw zkStl?cxawUdfZ>5(u32pk^kh&k*GU&?`l8Of#^K%#`?9xOyb>s%B^(1R$?iHbJNp& z!lG{JBIowCo5g6?=`2SBSs+4y##LT_>{-@=tlNA!?%lM6lT^8O*7tMsKu6N;YIW-B zIktDRn%08o>P^GA0!EIqGTCqb96@@oe7V>1b$?K`n(KBUxC)6}cp}-dTcG}cB^3kB zN=t?Q1=>|Bt{D0qe#hNJSy(grmq*A_827B-rn8A)!UfG!OBWBnkH2|HvW&#vx7jl; z*FgC$wQ=IyhS(UZ<&lUyfYeRuc`F7!29upcbOuZ^_XWCuE^HevR^k#T=5sE_ihUO1 z+JdTc1N7xYGT3bjzBm1&M9Z0V0nIF;{M}c0n?}^QX@#JT^^?w_a#&Op+Q&gw%Sq%e zwAN}<0k&`kv6Sm>7>&*>%tmtG?9uS^lG*kR=LdCSwl2Xp(Tnj@_yMXiiSsT)|Pv=ke^S#%>#aN7Cs46d3yR-cx%-4fi z%GeJQHu7YS%r}B>u6wXuLqOT4@1Q3B%cB8Y2>QjB<~LuKYA419Gw|J(+0XdboSW9t z9^U9z8GWexaM=u@2+<96O9000!<8E!J}~>1(GA_&PrZYIaHPE1+z5uRcUJ|KCy^k zXAOxAUAwL^mrDh%T1J5|ET_+#zAy*@qA`;C+{a(fWw zWpJ(go%TifLjh5X&@IUIzVIH6zLc0fvIuHu#8HXfc^_+HbZso6|H3%x%VO3@ELd!M zTLl|thd@hAO`2!>=HlH#9P8(`18H(nA$)Hvj>J1!=9gAa(EYJnmd(zEeRNRBBsiL} zZ{JW?Q+bR@>9_&fWrNoOAY7J>Dr@uaY=kU`&AV^`)16)saa8(+pfJKFrTt;wj~d4cWC%AvZs4Tp%INx(jTmIFd0F*8l}^X_m=c zd!ntmA4dMHZ&Zl5=0lsoc}_&5wg}s?E@W#X3k;0t6g;v6NNUc_(Jy9c-wK4>xW_^S z_ODn{nVuL1?eSbP&Cy7uFboDCXxdDfX1S1=9cr9Y94N4+UIA$0;I{3Mx`bv6Wl#HD zC-ZE^uiHkKw=sf6VDP923*c~eiF=gX3t^=qiNoj)3V2zU$EAU zV&k?~$JU~&Pvq;&kHX%HdSQr@6cGF!@Ao#0`pLbPu!O|Vh054?uIEL9;lIS(O>9y8 zre?hJCv~6#cD`>jc}py_!pq_3k5V7q`H~|qlAQtGhMNQb1MJ(9RWh&Ja3gBL>B3*M zx87^Xe4%dyKCvraE{jmB;c$0eh<)=ql*^Ci+($jhRHw+C7c)hCCUQtVg}Hss^7H_u zBPB49USbIC)1mXdF0Ao@XSRVwAzS(>KD5Ej3@wxi;@#rH#VcwTa|Iw!j%CS`&0+51 zI9Pw~OZxbPP4e709z~#r=q%Zr>Mf}gas<(XmuV?F<^>f|+SGT?H+|6AM&^aIQy!K3 zZM9(k!tVBUQ+Tf*C7B)I*>W#dm5KQy?*L(1=C8f;tfWBxV?Ukt&9y$(=Y7sGpAUaa zG8y7ywAbu7O&Ui3!f$G0X{P_C)2A{4Wb@EbCFK)Lu0n$Q`0vZsC8x!VI29}XV%d** zlhmVX`cyD>=p65sU6vrMTLDk>_I1Y|rV+I{1h!jG4^;t2=U=sv zrVzI3tiELfK`EGvr}L?G@nI;=Q+|%hfXO^QeJ)MsftYZScfulu#{`HHSxK(Bch6Fa zZ11o&blq=#2Z3Aea~ZU^lff#fgZhUW<>P2*GE@epl@jZlEpd+!($U*`u~6X+kcZG8PL&|20YAZ2ofJBBS2nKK$#&c0qPVc z`=5C5l-tLXOCW0u&AXLE#kEa`24&Vj?uzZ_-GDg5beE!v(`7Cub`mbk3D>bID|el8I&n9 zw|IGA%;thYJ^&=Z3BlFN3dmHi>S`=l%cP29{IkDFi6fAcR&W+n3EN}E$0 zIZtRO3&IMv`S?V1knznCb%kzSfd-%R$o7R%W|n&+TVTVj(kLd-w4~k z|6BRO^2go&MeC8;?3ygRY-Vx~(>oW(xmk=}Yj$D$*{e&qoK%Edq8U5b-j|UG^C|Ko7lRM9w3`}|@#O9! zt|`LkZSzC2gfpy;a)LLw2w4*`e%_SG0q74fkwtaoaC=zR(b%|&L|E~tF_5uQexizD ztq>{h3v;F=0~1HMzs2PnicTDcPh05zIzCUZXcDY_$kLnry*$hMyHGnzF?^WDxH&&* zW8f&;Qismr+<2oy7f9P5*!Z#rJ{2+`KUG#_Zu`j|=x2TJYYzh^RFbT@+1uK85+8$A z=hHf!O35Yn3Aq}I4>|JIvEOov_kA)UuFy){F>ayz{0%)@lr@KDlADlr?$y$s$R2mvfMK%`ipmI8o|DzANi!GL$#m%0~qy zi%DjLD}fV6QPnqb_kt$N5>P zs2)#&(H5`Kn$d1uO1fz}#nw{W8x)1W%}+!q&9Z|I#9XQGOv5Lh)2N{8rS9%FYC7xm ze*YTkSsq=tYwX`w%$wW`k-Sn>Mixe~JFLiSzwk#tD0l~oVx1o?so22U8gHMDxthu z2ea=%t|FJ^(1fm6*5p!Liy8A=A+Q36zZXvTaRO0&?RPEmx?aRrf3e>>|1iLJ)Hi~a z8>$&2h)eAo=P<_0~_G(WCiMDu5*k>Z@2@{e zF`{?aeVjuQI0UeL0}A8umYly~swbkav4|d;UaCR53$?@I8s+cTIR4m=8m@NhR4KFO z725Zr6Xr#uKT6*{mEuR8sor?LzDqfrl5L$rJa`n=G*(klVhaA);te!X{c>qI@(S zn)2P4@wRW7^ZwLNW2TlDNs`xR=ap=#+v6B>4^u5z8aac928G;M8E|Zo=3ddYEk1|GG}XyL*h&W2APwN-aFIBwwvx zG2@##d}e0pk#I*zG!zqr>{)koRKI?0Csj+aaz$mSr_91|I=Z0F@(PuvExqA`v{?qf zQp-tnWhlo##yWx-*gE%u?omEQ_roLqxbZt9#7pw^7v`U+R~KTeeVanGcp-j5Pmtv? z449Ga;I}usR=Ot$8qk6NJFX?S=+U;G^bm^GXu>ZSyZe)_?^|z+vp&+zK_}^vq#p!AX=9s zdhq)fQ%gy^8g9*ta&Hqk-LFr^$;1ftKH?Ccv2zhvx3uY{%3%hewBWWqCwicifDsAh z@;8yS-H+{cdG*7KB1u;#>Gd;OF1{8vk?$;Rtg-njCkD9!!;T4EsZ7lJNNbWj#)N$~ z7P@`c!L2+B4`p>I8pa73PoC?EL#DSOvU zhM-zyd%;xsPd?R zC-_q>jaHiWiS9drByr!t1-r)8(8`tIZAW)!DCGA$vcUp{d~u$%?~l0&*=f^VU`gL9&Y# zK`(Ty?>XP8Oh61VRNCz;%>B($SdQm(B~GlQ$N~&*NSzj+wuq^ z<(V$~^!Bl*6iuxY-#tM!C@kyp4DmC2z$aMi%mg`|Fp;(YA`TR+CY-yph9E z-$!5G5)-T!Rm483Sr%n zLDuj30uZXC;Rz>_)3O(=j{6%?YmyxS1mNKCKa*X~!X`mwk%4&5%V`@jn0oEgEL!== z{+=ME-xvo2bk*N?zdej7`d&QsWZw0{ruBm!u*Nr4s>~7lot9NMbUTQ=Cog>xL5x$5 zTq36tbsM|uf@TPMYwtmJ6!D;2(h$EEm-8=y!!P{@LyMd0SIP#8K6>4U=??p6T)zNw z(v=0gjd}NX6Avv9N%0V)D@UaUg0-d|{l55<1o+_@g6{*WYbf>BNREP;@-x(+&wXk? ziI%A-FV9Ir?%FPvKOIrcCU5b`#7ceAETmN7gM~t~hlFPh+8`Ux*uS@i57#+aeef`m zm)}HON>)0wKB@&=iPlxtUM>eEclTHy(dnO9w}FnopM1KRHxZw0vR_$(mJ3WXop%fg zFWTngopSbdJb#L+EA^_k<$3VslY2A_fg%UFxNR=HFvbx%20c{qdU z70&uR{_}?}zmc)jaci=gB)JLFhFZ+tP8JoSIAuXM>tmuiA%mpkRH*PRnRJayPRY~^74}IHiX!*oOLy-{9X<3$#sSye zOBd0e8sinG$cj#x&h@wz`*CJ|)HUtU)9jatV=*~vy zh-ULUGxWW_kdCL-kieU5mGC&mM3RZ35`KsGZg-n&X?iQ>4-#5mMO0WRQ5@^u6-7wS zRed%3;vu4AR?mgVv8SK6<9uQrUR=z5^mtO1aYmJg@~M(|RkWp-^Qu`jtxBCmcuoI7 z8EbA_(C}}~=*mA4HhK?)<|biD*3U;oS-2;Zun?5dlJ+G}`AdVVnJ+gBVtez%=nggm z&oM5xwC^^D+5J4aslxGv6qf^MeTFYlM(If&NdR^!Lx z(UyrU`o&Ub^xI4wr978jKFJt3=s2mYPlS@6Uv?={51(@NC_-{BeeS+qt_}2GFLB>+ zM1<3!li&0AdR!u8;dhC%xW*81FW`tAA2mWJ2r;qSM}T(N(>>MOkcaFA9!9J0uz@}K zrhyNQClw;`hRD;#oFE4>%Z_FXpA%9#R_juDI$DvXd_cv}<@!V=0p3M)YeRFgqlHX5gMfTzj z$}1(kU&H;AV(k44we3^8^L)@#J}$;{<8OSMv7uff!bH=~)GC>W7Zza;HECz{LmrCz ze(N_n5a^Itcwf7Oaw2IWHPB2?=!uUN1$7jnRUqj7N!{pGlfe1d8yk}|Lgm$n=o zs+I3qy9R6Bi*hQ=t6pv*l>oP&+lkEQ`eYtq8k=xZ+wTFreO|)5oZPLTLDlV(p6z5V zZyzcWL0vb-UNB+hQ{Kj=$a3R=fl<*m*!Xc=WFe!nIbXAS6ecdjcT_JFcy6&qYd`nf znY7jTAyn40u#^FXUX=y`udJ$tt%D=fxB__R-V%%}msUA@U5O!t&OZw@rx9=|zIDl@ zJTe{Jhj77CB#lb2uXx44Xv`tp!e`Vj{A;ZPBlqe5W9+P>;_9+(A0Y&Hhu{uDgIn+r zG&sRM1b2tvg}YmDCupI81b0n>ySux+P51r2?tb^)@!o$m3Tl*`bM{$#tvP?Qzw*Sw zM>-LE_pWoRhk%Fd<=IR0UpMX2h+jo`*&;$Ia01+pHHK`q3f{$X42ZLfykjSi_)?u4 ze5I^?YS^N~IGt@U)@oeMIheNc8(x!z9HFy0m>0WLbA!tFYsKpcs%NSD=5e!1iuLJs z8)EM5XLY382xkfrpA?$68zB<$`pDUF=JpuT(3V>(DdimGCZpDQZtah=-u9O4MH5~b z>TF6i{e6yA5uub8wHG;ic0ev2K^GW0-0<+86h z^&#k9_zgYM5$;_98{=hPAzv;@agXcrMp4u~PAGrOeYVIQTvn@HS)~x&oMRx|+9f+A zEi;mAej5bz3B{T6(J{iu{@z^Kf)U*s>fONfqP$i3`3-B~n@C2q#V#!Hg(d+o%xwP1Vx#YkxI%mZjE3d6i`%E|7T=nj(MyK^-IO(Q+8?pu6EI4@^{AzhSR zU;y zUKg#8LS3+7{Wk^DTNQM;=h{P(ViAk9bO*OmK{Bpup_M{b=Fmd6Ui;1mh3=bq?+4E& zK)$^D!88}SnPoxtyjh`pOsh-iw!1A=#uA}gf0Bf+fh~U6nQznq&T{|>9@YWQUxb&x z?N4B`d2dCntx!)VkKafc5U6@VMU8djTZq_Rsj1wt?_*w}lr{u{lYSLlWVT8m@g|*L z&@0mZ0B9~wvVMXj?it0Q#d@Nh__JBH?v3akJ257AK*#eWXe%ji>~moi#d-O}?6pQ< za-EcFr^mu~b59C4l%fI%%NzIo%%W4pg$K@sE*@4SXoB?F@2tTXT1ITM_Ej@!lg|5# zxvee++|U5JI2!s98Vc?0H@M%8>!o9HV8n8jPHwavaN8&-@Z9F}98(sFh;zJ?t+llq z8FoWE+agt&YCoFNboETUiu;QWh}DkQ{etX? zkX+YBkFqyVUs!+;55YBzmF%c%YT~9fHk`Z+JqR3RETr?EeT-Tqp|d3hH0s7`F@@wR zxGhyDRd7v-h0wFX4an&{8Y!4zjcLmc0%|IHU02 z@!bFhL?StjeGC$7reVj%5VU%<$~5~HKm;s;`)~^-$xfr_AiZ{@UYUCI&3k^Vrc=r{ zqF=AWT~_Ps65Nq0v7w>cOa3bJhU{|5UX;}L(LXI+OSv^6yGJ|revG8IV?0EiwUvw8 z8g$4S&%5zoB3#|V4-;?Dt~U8@uE^MkGj==E(k&bkk~_5rK?+%LTJoCmWoD?wz4)l3 z;!R~|{lCX=Pt<5IgA#Li^-=pjC_92s679A-0QDBy3e)*>B3XNYTBw`3;Gq_ z6H9ZubNJ_YZF9snKb}fN7?k@xMfPJgDMNe4JD^h(wo|yx3zqN{USp=`A(P2bQWo*W z6+hy+iOh&FxRHKcJ@Dd_H)KzUVzzUjH_)QE>*$>@tQ{8bXIAD=5x-GAT@F!^KW?^l zaa9aoGv_wDIo&ceO|uP@Z+c-$zQ)23WtyS3yEu5kzxGOP^q%mV=}h&{Nsqk~KwC@# zeSj9d#SxIP|B)DW?^nUI-0!86Sg>YOE@9g9>HH`>Oj(PasxQw2s|X;!8M2@MOqU4IPg;!VhbGr3vOx{3H+g zG51D01r2FcNehH&-$uL=iUyDT`$Po2L(a6HUH~d`ha8yoQ;6eteW&roy%+bmKaJ$h z{*Z4jZI!w2Vg578C^0l3_>sL&IUiW9O|}J3(G+t5Wy-s-WxIUtb0k;3nIdqsI}N_JbVK*h`Z@t2kjz(T4ol zE$*Q5GS$Zb&aAQY!5eq@G1lQD9nRK#2TBxoymn(%fYqaNuUX+KI;vUkkido3!6d0O zx5T@H#a;M~5Yy{bI{_lx^5A3?l{stCCVOpF$A^w!97ZRbK+6WEP;gy%aJ#mG1ngG) zAWK2Z4qM^kvc-{E@cwtEufRct6ZV|7?Y@$IyccY2*aGP~s}3;s9!IxcrWxg<4Pg>t zJ~Hy;mz;p+OmnU=oR<(X`AuTA}%DL>SBYZ9E zq(oqul47fw_S)QZ!u*GdUmd-8y7TO+n;&}*CHh} zp?V^(^|G!3V%KIH|6Qv{N7j6E|M^yniA2O#i;eVVZgO;Eg8CId^l19Y(gx|Y_xhUR ziHCd@x|lQH_T3p=KIU) zYvvnw^OM_kZhUMK6_{ywHlbM%GgfQdC}@fMR|Two4oBLy^wK%obJ0(KHu_YUg();Z ze2HEZEA&v15gm^Vs)wZh!TYJBjR%VdDw#N^V`mFQfm#Q)_4&;54rGQz9uf>LFSkk$ zlc{MDO4M8WTUvgQ@?#<1ziX&9-0`XuM2Yi=SA-|4oqDMTKG59K4sie^x}SI+erzgU zsv+FA!3QyDY}azrJNvy^2WArxA&!#R6eCOH9jkN%2u6cs&lfjv>pBU81yz-Jf1-)1n zzR+L#{n!Shp;J>4|KD}iy3ArL`aew=p&UEpFHeJ=XiJN|FLyjHSa8@alHqw z@qErvu9ch2=?_B)W<9<+$V(#tm#sF09$(kQD-^j@ocyYLCrT9M=()#nlrE5!&%^ss zK%&~jK9w-;OhO|fBiOeKTy+YCLkf>tnJaXuUmq4wVyi&_4$4@ z#n@_=9&xtYNAXd^asx{v!6N=ot>tRui3b9UzaRusHPulhO)gq%QI%^kvo5VTE~}Nc zBB&|QurJD8qto4Z@2Dqq;rGQ}@$TX~KIdlmmJXKF47-wrD9x{s8>MnbT*Y$HR9-=B zVn+wNqTS_pY5~7e_a}eP3LrpxWTO~XHQ>)a{hK$<=Lx#5`{`zMh~N*(Y@n87-yL%A zO|k`v;}Q-8Vy`OUXtnbyQ7I#)qllW&=_pb=&pKM-T+*~ZM9V@?s4lSEO%qDluQEu2 z(2+eK?k;rV0=|9*FfM5vhLa3<+NM2ds{Gv`>NSMJuF~aJ_tmY?Y(J@8XaG-xmb7^P z;t7%$(%2^|8h+#A1K@Fx+Ux7KPT`#_>xi}l+^Sj^wdAu9FbC~F^6)$vSBwCtqtTEkMmw)J za9wO6olk}M+>!h#D?5!DH@XP7ja^VTS#hkfD+ymV=cJyWqB!2>%aVa)uO*_j*NGAj zAVr&5X^_4H{zIvoVz@^07#)Huv56T~zyAfdt}WY1NT)5_sIM_1^Q%7P^{PgJNbV;M z&dnll`d_-8`+knZ>O!NvB(Q=zg5Hg*% zPaEA78Bt;1Yl5 zN6>bU0b6L+> zcjiTahqb@``uK2LWpctRUWa#k^sC8qUCuA=FBheeV!f2Y?HPb#{_I121n`RdT4#ah zuO;h8@Ty7(%|Ti!(I?mEys@(gjtplrFK_JF=)Q^Hex0l(8MjkhG260KoK3|W^mjkO zPW+1oYP{LNW*el<4oC@%1+a~mD+)Cbaa}h6mLkQD{;Q(=#fp2Fp{ z`f-^bKs0s3cG~@t*oAawYen!oK!(NB6QXK8%IpF7fNQt?NO4LP44hM+hu+*RJls#{ z;#~0ME=fD5gGF9@ZVI{-20tWb;$!S?b)}`p(#Oz}$vR;%Z1651u}nZ;*TyOEqJgPo z=HwEn?5~nD2F{y-qm4}m){{bPgFwK|q06pwdUPg_h$4*TylbPHF_%J=am;XVRuN+S z8L_D2Bcq(5cy8t}uH;Rxve#Hm0Q3jE@0dp25L4W>0oh2aPP&QxY*;=4hxsLzk*#YR zSAUaIAgC~Cw9$+Ti(UGaBj_W+H034!ljX?LeqK_U^H zZ54+TR^HWa!Ze|+4e|7+zzLJI;AmCk0Ldimo}Cx1uh%r z+GbaXA&`XZGMmR6G9O#?+G3chO&Pkf;wg;R;oob6ip~v;T{KJyA0s{APkAWvBQ{8& zhf)aV$ogQ8`952Jn3X<{!-$cNq+9X~qT~3Wtj7GZd8{Pso*PMe=Ut&@j zbGK(*c1uS_sHemwd4?C4J3`Mxb8UxOFy{? zxt*j@@{sv$>%VE}aE_r(oY z657Rm?n$35Qm{L4{O^&RfH|lD)>@xX8io}R@vP`QakqZ74HVUDRZiIU1pG2YYJ=Ad zF(y>E^A<_cBk}+cORs5@(M-`_#EP1o&)=fxX`e#=r}HxMo>sD0#M%=u~ot#nzq;9>KS*y<*8xqdH`>JSJ!$sh}-Vt0j%En0{+9j zK(%<~c{<~wK)`1%nruV`RvGQS4;?KI!va^kevoi{lPNLf-s|?8?Q?!B;BlrSLtCzt z(f@5r8Q7A^gvEOUc@iJWPFK+bwV1BR57-nCTS;!_{;iyej2M|OT3lr4WR<{s_xd4y zSt%orQ;b2(hUY#3-lF>N&sa$8Xpv!S{G9{riChTGNBBy(M&JFP;^*e|s3Ta`NQY z{*|jW?Y4>{TWXb#;WLIAu~2~Dp$wNeRBxL7%DLY$Ffyq(<|L=y+TH+I- zF?J?or(UYW$#AauBm#H;y`z}8-YGHqFv}}ptQ&yfe655;l zmOTf_3v;dF6+W$$b7JIO3So(Uk91D`-^^D(g(9kKw(=4QqQheX)oIVg1CG?HwDSL9 zH7Vwsk6?x!MUyB_ew5gi4g1`Mq2ikd!O4aVt`sW9(G5Sgn%(ieYFa^?^>xH2qVj-y zJFwu-3?x&nBvpJTm%bhZkqj!^TA=v%nwC;N#r7wN>ptWgq48^F7u zj{R}KuT-XE)n>hgyL4_}m4Ly9%N{#(^-$w?hwEm$vR{U7R1Nvlg*CQQHd=hx+^W-m zhV9!*st`QfnX95$*S(zJ;FV{Cu?<5fR+b9#vMNr*+` zwdf?_v_EDZU`5soW(#VAZb6Z39K*Na0t6b>khAaTYMP^7I3%f7uSRvy$d1C67vyR5 z9#c+s0X11(EnwkMl0oeZYnW|?HXmrIj~^OrC#8!YVdx4r)qh6B1-g>Ny8K%m0v}8| zP^n^%r<;^bG^xqG*v(jE8n2Ch5#3n6*H|FF53?spTXkVNFUxhVj5|K_gKl}T-Rs0(~r}S zqWC4t58Tgp&5+onE&{SMh5Myw(pSZ?kqJ3ZQJ#^X!AMF1$Kd)0l58Wv~JA;$+xnqb|9S`ThZRyuD0!li8zW zVf9%Sd!`zyarU!ia51_Y5EjXtUtY@m0K12?wuqo@zbb{1qzZDmqj zBSh%IIKv&=aO<7M=O#t&bT_nlE}euAuP1p^qpVpTEO=589-r+hb|T11;?U1H`N5ta zOP-})>POu1oB3e!$W%PP=BPZ=K21=S`{G;e6V=7+{ia_MncRZ)gXc%42pOzx{f1;| z>GKw8cAU};H)-+>e@jO6m%nX&6G>;)J9*e#)(q#g_@uDKi_2}9J`XKT+?iSFeuk~2+SEVxz;_E$~q9V`p7*f;dCaRNW=UW1v?}E@6hl5Zh%K_$-(qRdPsckv5 z&H?8A;T5K0+4_j**PN9RmI;tNk*4##xk;TnqXg!QYt!o6@6+>nr8njo) z_v9zLeB1boVv@rs$@)UKoC|0y=^h(mIrG0~nof93M_DFF(E9z|Y_!%GE)n*IoLw)! zP@iXnDswn?iQN1+`@UkGu6NU_ZHab1kX64*-`j* zSt#a^R)&GcsiVLeWh(^qy4PO3>75C97x-rQ68}(*B|}`H?Ti)0(Bd9gof0t!DR+9- zJhKF=wT6W8_iOlf@w|Xc75f!~D%S(5Aa;3taL6Tc zy%P)v|)hk;)lyB_95a?AxHZXBTX8yV$ps=?SM0?c7^ThV$is zt|;|6?HyIFm94aHv;fE1e2b2tXi|?%Tq?^ErVw9CnMf(n!ek+1@0l*uTl8bfasRwP zZ)rL-w!ibpJk($XsZ$SE?aorJs+-O)N`s%tXRh%ev~;kbCmfiyn2Ks1GMk*76qqw( z!lNV4o!u&@vO7m#h%e@BwMclHPh=;A+xoupK(^*4K?|ha{3Vb=h~8pozBrb8w%|gw z!Je|^DN!&dFp6R?h)r#Hx0{Wy)Ox9RX>rqe1>0Xe8FaC^Gv4g|E#PFZ)=ahk2BZDx z%JsT!^xub;VtRn3c1BL-4{{V>`P_@bP_fzoZepN{$y?rvYnC{JcWt*-xh8LGL zDYOish-oTb6{sGDGY-Rf}Tc z0u%x6FVUX!>kOv64t|He-49*W`5AS>VuQ+Ff%ho@!aEhgd@g9R#j1&_{{$s{q#$O4 ztPhF(&;SIHb$Si|Nb(r{N#;{u#7~1EhYVYxrsJANQZsMY|GJC84B}hw)WN;A`xwh7 zCkcBl#=Y!0W^+3NMVsbnXjMOW6x?OExr(*Qn?kh8zn@S4viRy?Jo%TP7~Rk2O$JEF zn{T)Q@zZ$Ua*wXQj5`B^CSxt4B-%6f>!*Q>G<0ZfuECg$18wl0mCEA#$klSm0YPLk zNXufd?bx<~$^{$IrVuE|7TS}TJPl=P?9tV26dKq^B3o+rQh!0iGK`k#d}NWMP@XMK zj>KW5-k(3rn0z;Gw#NGh`$!^sH|_4@WZAGxZC%hH8~He?x|C@y5uf3=b!bE=QQS~W zc63JYl+02RER!{FM0nN^x{oE)$xZ(QZKmZ^Pq50+R9%2`5@~)ix1*21-H^k>`&Sz+ z{pXX7mESLyWI!isJ$n=_I{)asaSlmdMA345dC9NJlE_cSY7aNgB%ZL6@;2v`slK0# zg=j*gO#PEuRu>bTI=?P>o>Idw+M>8ekiD0?C_mfFvEIlnX$x(z;)T{~Uripg4AA0g zZ&&%mjwevPm=1UeJ(bG>)IC>A zlZTKzJwj;lci(U5z8O0F-C5MV+;})l7O@G^#+!LI&Oq}!WoBi-@dR3WBBBBx3mEv0U~p7TEL8#r3`tGb54_J)0Wu30gt(jc77Ow zyo4&!h89nvE5$2J(QPAiq%j^eCU7F-A^s(nsmV<}{-y3YQ718TRC=$!1&G{s|I**;Ye5oHXHefuy*I2W%g=^Vfr6#kpJ7067 zI?jA;iYf*BUPq_$$bzB+aqE29Q~&$O0fIV(`tps<;o?u7dOWWSxD7bzmYn3(UeDsf zyIM60dtTC}i#(xb?w8Rh-*SI{(;Od~uWNAJX{NMu&{`kaOPxgHn);ld`Yz~Umte#1 zJpp<}vO-TssSGqW^gN6irnRR|i0KG_Nq6`AQjGE^j?gpV_#(3Dzxluw%V>I0-H~+= z+Tu2D5_SbVBo@Rk-b{v~Nk12n&Ri}l`R8@xcLr1q6uOXDvU@2XUOom8<8nkx3(WmM zVts1xJlmtAivvR=(A=n^Plh=RSPJ9Rj*faA)3n7b5$M8VZc45_AJIp8n3Ay7j!9Qo zgk+{$&;3`Zs+3hU`I81z6ao%I&+V9FJ#k**uoK%LQ7c7vP6R&p|H(7--AK2M&hM?S z!q3+qYapm4HD?AF^8V)h6ObyGAwO082eU-eHGR)2i;s!MAgzwk*pSKId1#;dpU;{*mFoUnomby zyU!;s=k&=g5#CA{Z*>~-4;!6yZM=>6@AuN*Qp8$fi<7)Z2xog0YjzV@GEvQ^HN$B~ zo-~=H(s`z}=Fcs;_v_amoiedaF|5pCc7L0Q$Zy+@CEK(+ zqUyByq`OS?yE1*bLRcg0>8DQ@nbUtb?e=t1{!GoSXhvA?Q5d4XpoK382o0=F#Jrw$ zG?=ORkW4@3B)E^_fL?FKQyw1Oh>WgnaoP}`ju#>5+<_vyqV4s)E_Aat#;~8gR{LV? zK{tV}6#4IM4FjVOT5VttpG>}#Y1l%r(GTuSl7jZj*V-C@s8mcdBbCt#uy**p?5-#OgJBaz>Ljg4rC>Vk*pdTl9*|~`;Ns(+!_S@ zB8X?nYj!AoBP}31W(F}Z=@hSk)j-J5#KTFfhl~x&a8RU__sSb@n8G1TeuTy_=i8E` zfdTnNEnZ;cE;vb8#^$JQP|heU_zr(O7ueA15Fzr@`vRX;6Dx>$!L_}!=V5fIgTGQx z{ivcEB8_RMN!k^S%d&**xTu<`RfcV_T#EBJA2-NlynGwq;eA&76ynz4-2pVoJW+x2r4PH|VA8Qa1-aTbS z_tL+XV*N+$^ZK8)k912D+KzvrNfzT>GJNdeJBb&F#>>W83)mhwWV9Wb5E7RC?zEtm zFkRwk4QgG>*$kN-8ScilON<3IKYwQ5VExNaRyUG_i6Iuj4=ftEUQOZxUQ_dCq>AEUyM0&*0) zd)7=1vRo~SQhL8d0~En_jsXC|D0sS}#hr*JhE2~2=ZITH*~(HK1DDxq&Um?~$2NVmLD`=!V8m?1*kh ze7`_KJZR<)t=Qul+_%x$q?WvA(QirBhxXLwCDc0U=c8AtNT%*7Mua@({C-uKMuqX zbg$LvYVJA(GlDg9V`%0ORt&l{A@Ik*QZ+5%8AgWwo~28Q>jQb*AhhG9_(xV4*kA2Q zMcIK5z79uX)h03oFp9aC?()yIe4D0;rbX<{!enS@AaQk><=bN@d#S}JN zeI&_R@v*P9g@x9qjE|`n!ueaTE;Xxv{wQFXzan?3wev#tK*v6t8BE^rIEo-mla|>OKgfWKB&iXm>jeU{0M1LrlOgU z`dcg~MvCZDe8~D*+Bb`_FkweK_p}j^E$=um=2ADI|BS=fhXJFTN)eQLH+_q-XAPp!ncF1x!6YPpX@S zJoRD#hQX?9OpM6wpk&HRNWWBygyugO$Uep7KRRf~B8=In6*_O4oDVXJfZz()FyGda zzx_-f_C^IHES0EQDjAA>ErzepJ^H`k&>(OqO2P`1HiEbu-BISriGT6y?5J#Z*p>ET zqwRNrcI45EqW-XtT&9sjW#1SWkz^RBQPb1;&B6Lx>_(omG}DO! zGMCIjl#=cQ3A*|R_;)Nd+4HX2b$?A=9=}=uTr(Kcf_NBIuyt+^^XSKO4Ic(HNrc}y zZU1$Ps~BHL-b-gTok?>I-JMUoKm4Zh@wZcU*2nYl+!yEFVF7gSz{J%$F7a;_{iD7K z2wFEm@~`98Sm$Hp)*}q!t^@?={;4%ry8)=qW`)gRA?c32M>HNl!qyk3Ko`P6+9CIcazdn zr?7)+s|cvXRbk#VO8ol)`26;(cqq>KZuRgq(7}UQ0 z1BeBXgn%t7L-}}8`E0+}>wuu2gNY(Yi~%!Hj+1o z_^0UV3Gk(rT?Eih9L>qwtCR4vN7NB>;+lD}oaKT^s-?%g&2&c?uy6wkpS#y9nn!+b z1IDL`DyX*IJ84|wO?kqr6Mn*@cK#dJ<7Z&|4|V*cJ@aQveKkQ zro+31#x(7s5~uAcuuzY4^9|#n(K?So1kWMjkQxCXk^Uk zeMhX&!)9jM~t)jApmm=f~yfyw(49 ze*eQn{aCx>7&bcyD9@`x?^X;jOUs7@q5(u!s99zu$=8Wd5|rG5iTaV>@j|qn)pk@^CI$#Vhr{d_k=KYNWZINo7l}1 zet>A}LrZTd$Z?uR+*B8c$5>B(Bu{z)`Yh#71+Uhk&zb+r5&zG_@OW%~$ccX)L5b-oIgId_usZnl zv&LeMdP`c2TWq(~N>u8(^ddl&m?ZJ-@0v_q#EG$bp?}8OR{~H$t<)0&+ zxVlEF56#oNKpnsSOKFGdt*ZEcTv~ts`2X7jQl4i44_6x`)%{F-kXyRGS_*3-xjF<@o@b6 zlRoKDKT!5qI_|^{{XWsA90pl>*kBL{=_^t%p^4!#4}FXosz_DI!;+ zS0^J;L4&0lBq>)XxG=sMZTW7spYuV`$a-lt0k?z6*>>h2h>)>veE^c9rSp>VmM$zh ztq$ecx@=dY%MqI<%_}hO8qA3lG@+LF>c71lm(O?DQd}PqMdb)5$S^LiU$#?Y+_pPf z?lkFXZy!kGu^+ftc+e{*viSm*q=bgEKPc-3nX0LL`(6Nzh>v ztr|%9ryL);7rY-VspZ_}!aUtdH7fM7uFmQfWY+rP2lP42QxKV(f4uo*ldsFKmC1AR z+#WQZXAS_U(|@W$-_V96XmUp#YSBPfJ)yH+(T>VK*W}pifUHmT6Cvy+DzU-(&|RAc zNv_5WP#q0Ejb})e2q%EOXj1u6Td%9N^Y^aze|->D0K;iD`zhhIrb*IDk#bQC4{4GR zy>g*K22_gXzm%^&&D2o>H}TC@o-rGlcUv3ldvr>JF?rH^(HzBUT*yQGbH47J%&_}-K`My63EZ24?tQC3Vu0u+Flu~0XRIu6 zJqhnM`EWSb3BK!WG4{J!%Zd3?^-8wcu0Cgt%D7Qi;KhyP|^e7!ISDuk==qqml9n!zsMe#Qw&(6X3s z=5dk0nHrun&ER1Ee%Xx8(s1{0o%{d3GXC?Kw>oH-yh$|MVBpGfaikwOKAgnbz6;=o z_|UC>ZF4Kk3v)f6+F&EX+(T@m)T*=jz`Jz0A{!8b{j4sX07|=O?qqB4_cnjq z=Ot{pbYd#|5O;fssgCy@@`=OZrQfGQwFANRB*O@JadyM!T$yE>4{S*1kYYG3kL`(X z))N;@YC}wz0GCUH^Z8F{k(upQ9;|98E;Fk<|50wT7+;s5A(}Zdo{jtfJ)cAKU?X!a zLWg$2i#_$Lz4s>{ZAcc7U?hoVS|6UAv?1cVdg4R6+%C2ZztuUFQ49b9gPBXRtu`i& zEE{vnVayd+%?FXWYT&mCtd)LuT6}T(AB&j(c=9a@hI*Azu-RB{@X-UyYl^a5|M24$K0{zAvdM5O;$b*2%>~VC;9|W#j2p1|BKELVTk6 zrMEWbP6$C{X#wP&B7z?0uWa6~jhMl4k%@^i#U%_xv*g>5q7ex0>N9lNYVHtw*6^^| zBN{WvL@^vtE)ncd>#Qy_iUwzl4Au9+Gl%!4Z$Bw)}ffJj;je zvfteblGY#0^=EMRA(eLHvU`KCxfbHgm;qZ%1PIGY@cGVr-=e9IG1|8yPXILvoi*?L zbT3F1o00(m$XcFfDo#Ssgbonlu|Zh_vXT8Qc86fRnlT}Hu(R%&ucx4c$oWbIn(a9e zJo#3nipA!AXAR>BA!zE_UY*A0)9oVj2$r7v4W<^UQy!aHL6h!a$wNKawI5EEH}NLO zWt`Ew)lH(2_u{J$v{Y3Hz}o^0b|{D91UwCqHN9G~#WiaE#A2a;=&vPVH}?OJ+?UlC zPhptl7?lA(*d8QOPQ#n63(XQK>(np^W!fLF_BuVw&;v)D6q31ymfeWW;cxY`GCO0&gRobSTpEe0}Mf8@6<*AJyzhhB$HpG|0+ z65|WD@h-|Fv9Kzr>X^${gWOe8WRMhg2dX|}G{mX!pQtdb6I%AQ`1~%A87xPQ0;kvZ zC$d};b838(Ty`BcO3f}uNCsqT^&fHqIkj==M%O(#7A)mvFkX`BG#mHr8c)|P>?>nw zTv^sRzWElBsn;opib7yP?7lnNLw1t$=*_&JnE>70kP6_F{=ku!^Uehul7L&Alk=`M3;?8i%a2&L)T+N(&n?iky(c* zojrl(xf4e>qY`u4t=^~PPygFonF|v7E^EyIls$j6AqTi=3muk2HH;YC#0`+&2mLRG zBrDIClsjltzwXW!+zOhwvLxNz&sbN6O=OvA2Dr7fb4g1D)fh)3xS_MaDql7%Cg|&edNu*M!1T zBN&5&Ri$1|PDh)93y9A(b zKx;s@UQYs^JM7Z{eS(wrgZuYDyb4kY{g4bVadkcPTdvj#!7Y$ENww_bRcB3x#It}j zII?g@oSx#(%HN2gsaQrKvs4wJSU>mI^t<&Z@jSI1Ks9(}FbLxCsDwNubB=xbW#zg~ zhFkUI__wg^AASg%T%TT)*%TQ?K}$X#lCQ34g~cSwbXuQ*W77T%n?V(B0+9M&)l)WH za=v3oD4zs%v|pT!sO*K6woL7370daB3)6u&o;UNxa=cf{mK!UKdND+&3zZE(hMf}zXVczf zm2s$L_wan0Ed5@v^8iR(-sSmID#g_FqFEd3oK2zLa!SK$wtkdRtA6Cto~!rI1&Gf{ z0j;j&g{tUEU!M6qVOZ?x*hCOiY65Z}{FHB$d0kei{Cez9WTMEZelO13@uRR8v1xyI ztKBmU!@Xj?M_BjA@3yIaPsN=fDEW%Vcr)sK6M3Y5T7J0qy}2NIgV5YU^^KQ0_#F}s zs*PqI9U?OqB;CU z`jiFnNP{3gaS&X1pT2Da5hDvW{q2Qhs|*$@D7Y^vwju|>?Xn$SW^53A;BJTQU%gRG z3?COO4j)^U+F;99^Yf>lCCCk_T}Fao4M4!IngMNXD^OyRl%?1ZW=A|~6q+Bo|NcKv z*szG4CiT}rL#)0&T_g5Gp*AUCoUEix>C3%70v%u|>4^}}d`(ISqu;n|I-Nfun#snu!xpr+Y zih!3fU-?jSJNl{hHG)rn7geH`L8H^&=S^!ZkdZ*72jY3OcNUKT>37P`^ZJzaQ8}g^ zdAU)B=k~DrY?UjOSDMtuPRj@Jcgn!wT5>&xynESlg`3LBss8`^a<8t%k&~E39&u z?}n*ylf1na{hs}O=5jI+X7CLHI)q6kMPD3~_O#&6>^G3YS3ze*%D6O@0&+!YS(bPN zfZ)})q2W&;v+B zxsZwWMGNGLoa6ss1v_BZKWSg8z%K|%xQzv!)Jm&nTL6>f0w8s>R4Eo*?{kRFaYkfM(YE2ZAh*zNnn#BRbhQ2 zFj#a_qVxagy7jz%Hc-$YG$~H%-?I*&*Ml~0-Ywy8=pTn1v7Pt`FLW>Kyu`!?Q#jZG zwyF9~EaB&?pXsQ!@Y$r)_IGc;{t(*+S@wnj7$6O}wv|uIbbqyRr<@&FKvn9)v4?y% z3&83O^&*Nwo9?i?{c#2(+ySne_$=ZUmR^3#={F}}J~0iy4pLi9>0^6fVWeefy@3lm zIixxgHM_uRZ~Dav2A4@v$bq|N;U9zn&uy=EqK!8l zE#mo|{nda+#%Uy{<@BWBuC-};@bRN|6oLE`%^lT8qaewe>&Y(rHSR0p&2hQ6>(`tU zVQ+oEs0?QaAeU2*wwbDY)A?Qj=P`SQhD3(qVDLf>a&n~gI7UaP@%U37ruj&=P_@|z zO`q&`-Q@BBojZh^QVDa0XYD%Qp@by$ymJ`BFGMJT-2MpdMLt8lL^s*-)bS1GFjE@( zlo)I`#V0X~06g}OEc3R*Xo%v0_6*yz4b`q7l&6f85nmt6vB})Vlk%8opKs+0L%%D9 z-%Ynm_}3xSK5}9ao4`rtjFl*%gm^lou5VrCH=fK*2QRxUx|DR`dZjkmA%`v*__1>w z-2}L79Ah(IloCR#hj{zTy0pnFL~IgNMojTEDKZ7{j)~XX+21{U29a(9+f^s~M+~wP zH-EmdPrLDUkF?!0Gmpwab1a$)x2}9bN~>Jm`;@CG71S_t$=Wq~a1p~ZS?I>8`n72> zV`{8zmSbA)r*V*Cs-JP3do$0L%BdEO-K&ZZ>j_#;yQ|t@?foCd-U2Ght?eIH1RQ#3 zkQzW*X<_IZBt@i=ln{^_8leBnD8rQ%Y)R1d#^m`u03=-shb2y#M!G z>ns<{@+|NBo_+6qUDvOYJCk%O1b3?X@QmFfK~lT*5zdHAxX2PB>wWRVec8|EP0g^4 zF$%Yy+oDR8Cp$eSM$TUyQ;{Gd=EhKD9;^2;5v|~eo=3l!O)Be)qQvY;@RIR)H+hI8 zRiV&Mfqa^WwZ3n&v+DmeAo%-yN%ktov*b3shsfJ-F!)$+n5a|nGnNM41y2}AlOrVN zL&<|{VuT3e@IaIhZk)WU09VE+KP<0s996iKd`C7-#QUbIi)Dar# zoW?&7PL?}<#t63^ms6G`NOtmbPYdQv@@9Jal;o_t6pgv!hhzfSPOJ$f4g*6L8-7JQiF>v4@0wLvGKb;hA%B&6Ot!8jJy7C8;+rSmRnqqvtZ1 zk64gr%&5mFo9G%hq5?l?_Kt%h?f|Vd#JB(%98Cpq>(9i3@GFB`0ur*e;Sh)b%>d*a z@`zTQ<=O)>UR`W?Jaj0HC@K&W(?%K043#a4cor(qM1tD$+x;RW+F3d0=K1M&)%Yh`f_>$EZ=|NyeYUTXd=Wnm|P3=1P zCPS`~X{f<#<~`d`{hxzUO+N-Tc19(j^l7Yed7$RZ-X04)86Ii7#9$7Hq+g<4`r31f zZ^OQF^G7vv-4>(1J~&fK`fYpMUt~*!wZvbG;_sxsUbEp;gB-LVU*$!J3o0JqQQSV6 zZE0{>{PexO_Y-B1W*Gh{c2&AeO8xmJzA1KxlJ4%yi|DkHF_Xhg6WRmISh`H|FY4@X zlS~<@KftD}O3#)gKfp2rAD!|&@Vl=l2?Cv`+C;VAf3e*BIn0wtFtn|7H`)0@SNVeB zOpAhcGmv7uUf}2l>jX*~wQMAgenN#ib~)Lq^1jO_<#bt%@2dmyl=-d6i137V>#Y~~ zhsD*i!~q4z-?^1paTMsizscQcUW~zR=;i!-pZTXx%$3+0tt1v`*_T+a?GR&K4c5`h zy}Ent(Y0n`v@Bpj=5PZeqApErY@m6(7Quooq8`s`+w`LqkJVU=eIeZdLV7Kj&aAX? z2QCcbZf0QDgEhKXFekA@2+Q*#O~rn^(q3N@a^ufcbX}m*XD|E#b_J&BFz93c`7LZl z`WOiUV$xFcnFwH72d}0GrRtY!#`A;6sUWMJPqy9^ZstQML|P+V zcS8Jm9jOy1NF=wPQHE*aW)nHOd3K@r(NC z7lz(Ll+3diB0d?bSDdfe1*RE9j`&rKddbS&YPqAuCw~>&1RkwZWFSz)HZ5t~7I|nq z$!x}{*j;UV$fT!1_!|fvlqd=Bfp#NJ8{tmHV?s4m6H(7k^nlQUFjQXh9lz4I*3a|T zu}Y-n@WrqMa5Us1myD4ODtt>EF{JRZ#N1 zwgJnA(yOIrsmx7Z&o_~7$XBiw%qK$q8k@9=&`u!*vxVs{4;H^CfFHxoZ0$KKwJAQd zU+=Ns_VB$gVQeNgza9_%xM!%WAx^*iwN1KL1w9=kJdqdX; zYzEhG_BiwuV+1Mp7w;685QPbE0-u}?dZRJM<{pr|J9F^qI*l>#)$!aWurR~GRS~); z4Ux667qFvcI%nJ`-TR*UISYC0_xcIa{hjJ6wYLdc+~#4O9@3<_>T5Ial8QeYetzF% z1`+nk*XSu9Z18zt#vsFdgySr`-JI^cW-YtS$K|xZui!pr*WnoK)>g<6wk2@f^UjMd}SY86rZ+$Sbh1@LKVraQivnLr}$Nin0X) zvs+8QK48Qqo&DEz1J5$F0g=C;%mXJ8c#1iDDDpWF7A{Gs(z#XR1F=J6Qzy14}nbk zAjv?@@H+6rJ9ml%I{$=@d$xH8TmWQtav3Y)C4uIk*!E(R{^Qc@Rw6fzD;;YnpFR?czXi>;5CCO z7@3!FojS+0&9^8ev%dIfA15=vMEE-0-;;H?#F6flpY^WTY1&@z10^O)jRJic&C7K9 zvjrC~{!S+$lR9_eR~46BmIX^$=CGgN_dQQzet891Tkc&}@<%W|=otCv|Egwvx3CEH zqmf%zFpY15Z~EH)qnf!SF|6tw_7%}P5#J@z#zSXXd)~0`6Eey@icf`u&q5P|Mvc!; z@+bR*9tS`ppJ?z$Fqrn-GEJ+@iVZnpJ8P07!RBjf>HRMIVWil1K$g83EyardWN zT-<+oJB#W68aO?}gGELrImM{#xHK`{PkhtvCBDO#g6GEpH3aRwdJv?8f1cKn<&}Sc zIM-}fkbf=;6TfCy$@MZP&$wJ7gy{lXa*s5Czf0Wrz@aWmhYxjuOK@IXHNztJpW~}6<$KmD|1#R5p+#e zn3Uo)x%3lLt~hHl)bp^ekj>UUj&6e?@(XMXlgfE8F`68sG8dHSP1`hZ7i%3_?uSzhD^|uNp(a; zK@(x`_paIS2ir&vG57LOZ%w$aC{;k$YFg~Vq`R*VnmJu>eq)OzG+743!VwmfTBzfz zLdIo~p{)ch0lj!t0%neL-nZMF-(6NG`-PPF>lc-&c=%J01=yCaRos2SLJ#%RK!@_s z9K!TkePvq3+a0|YnLzF~S9K=a#vB85t3~M7^#uv)ND(S0&UdM;MJNCI&xK-Yjx;t8H;q*An8xa5GREy)}G{!ud_#{%_5#v`+ ze;-WqeCp3b{1&iEkn{7OZRTWk>Hui5!$uvE^SbkMHJE4Vlo(Ic&c@&9C6d(*PV%0u z_Zp|3YrM`_5h0+-LHDC)i{UKz@@381rf;y>(M)*+t2wvnm*mQWwc)ya+M~uS+0j$0 zZL)`UiVF2@EZ5P4`PFvARp}`*fkBk#urn2QZv7Ig_OE9-HJ2CHdqd4(H^ZiOe@q2V zRQp7vnmIVtuHHRmQGGvy=(Y#}k(@#1tC1}s=(ps(3$n=Sr~3o**-F?`&JMG@n!K5` zPQ!oB-2QJ(NZD%rk)>s;ocb?v-q-kRQIA=(ikRwQc-c`*_0J-8WD9y@HD|#&N?*m7 zp&4BcVvQgKbQj0dRR(Hihsg~ltB3f?qj?Y!YwF&y%qO(!b!G09Z4oeQjlSM zgoRbzXQqdD5t$i4PV3XeT26G~MbjABn1J^nhM<)R9Nn4;>2MGP8d6iaQ-#k_yFg%g zFAe_ubimlGs4?Ze-|=n}8vRS1S240L%^**=P;VK$ebr887j8{%7(#===*qEYyCC9W zLIJ7E2bUBRsQt@dJf^TKT``T>(K|tn_qqlo6R9`m({^S^>i0;_h4P!Jci~ANI^A3b zrYh~JYs6Z-w0e(O6ge#NH1)O!H*x&0larLB`i3bdDv#B51kPHo_}kM_t|)0d&z7h# zhbU$1EF5Ss^fF&(dexncbC3+;RtLAEs6I$FnAYE%EFqkt;xmn(_gzW?G13#gC+2sz z3DAO{H?|ycneSJ?#WkfH$RKVs!p0YOUoDi4>;*8`q>sWn!zN)++F~IvFkvbEUUZ}W zZ02mtf;68s@Y&K)H9N<_Zlm1Kgn5PHTDokF!RhF?PnxbQ26Sys7v|5C^FYtOh|KGT zh#w?X+TZ)pXgZ|tVCp@O6Mp}T`IWUBD6Sntx{)%;BSr5mS*{rQ3BwzAB^dSJ7j3ex zf94*p9laAy60({jE8OZIq*nW?ba1ldKLqXn9L)daPt9+TTW0lWN$|}r1Nvvh9@Wld z=0|f*)AWnbQa<`f5RK>$p)ihr*?nK_N0pM%$;ykW+1cefY&OBcex~@kXF{7?Vx*|o zFp6K3m4yJ$h`Gmx5NBTYME;x)anruV98M1XCW4@k3YR%f`zcUDPpl5oGb1Q*nS)(6 zMb`8A$a)hM)l96t+K=WBLpo)WU$`j0?*j`=oAnD3Ka7Z6p3>OCkGaXn*-?}v%lnJF zm8pk-ehR3l2*Ujs_f`g~b8z)}X9RaAno}ZcIs`1+$IIRaUS5Qg>=phV1fc$e^%tL3 z@0Fe&|J9#fHG=`0h2$ach4%F{DyNw&UCY1L7gF#_u0m##O3ci?=QCOL&c6sj3A1J9 z3=8y%-`;*(;`QQE{qc%FUE#S?oy_ChwREjc|HhOf_jJ9^n==;0C#5=AK}6k8 zq$h}Q8xgoo>lqxAJCGzTYiO~wdHF@#-VNz7J=2=ABdC5n&x=rBmy*iIA$HI@d5=`)jg)4LoeqA5z#ltNh90A?C8;$ z_WAsweqghVE+f1gDKjl)rGbf+!th!iA8pS+o^KATDnKE3d_YKKapwjwihZ7XaBFm} zr)zO&aLcvazr=~deBRfy-ALa|gYR5`onvY(Cc&>PEzloc0cMmvvsPn9RSvWV)`1*Y zGD#Y-C**^5M*}bmE)E)1%=`bw?PY!-W2TDU+QoU?tvUq>LYK10@S{1A3Vx)*{96BwkWoub`-j@ZE*3Yh`I~*%6kzK1WDViLD{!qU$%05zoV`D>O>&%hg=fkO znrtyPoKN`7*4jU>{N3@~{j0X8cT;?bU6{hM@u6-H!}CV4%$&X#o7jXC(>X~)C?fN5 zzXP5n!=vxmzmC1~>+NT}-O9f%fO9MraI7q@*F80f`#;*KNZA19XLZn~(B(Zqf6O~# z%b$!j=xW1Aob(b+#W`?^5$ni7MQSz^CB5r9AaUbX@aeS@-ks+Aj*It4+9lW-PLr*a z%onglBLy}=0l;BwJUQoYc+PK6$s=PXY|fxlJ>d-PNwkpw^m2`?uD;ZNS7M3Lpeoh>zxc|!R~F%FvM{d}!F)dz8^PKlT3qXD)Y-WBz0 z+ACLHt8e7O-OeelCv|cfqe>}Rh!`c|=&&grIlNF<|Lau%R`+lFDc0d)Km9Fn;-51g zb5F}7qKT*@ikg;>%%fhVB6gK`8z<#A)%F(zN>kouLPCTJ!6wdk6(SZL87jnc3Jln~avM#fzTRA8oXg&6$4)IRRQ8!)AExcB$ zj1xk4RUq)8{;e?Rf_B z&&c_HP+COb5KxVaLxtwiiR3{%y@1Q-EVD6L8RHWTo@rQ`T{Zc0mn&}i5G{z-TSapB z@$}1!NH+D|o}0BA4+SlT-``sXMrY*(&yF_H2?qDslMTG=x}Z$O#x;V=^SLd-w3)yz zJoLwI@b!ZDa{Np@;zyek?(#WBaW6QC=QA1r7E^@}JRy{r(zC_U`jDXxDF9y69w-K6 z_wvbkQp(J&ovZ>~(U}SDcd<`u{wTobCOq9eO~P1Rh`4|Y#jVaY2pZ1^h;!9!nEhY?iX<{ zer2*?Saeq6UqYDi=oRr}aPJ0*ynS4v8=)OF6j}4(k*`4&lMF~-lf|gwhRrz1xeHNjx!pc)i5{$iZN7$rp#wo+svxpKOTvi)?1pcnae;Q~0rQZAJ zuklQfq7x_ICxt_Ks>>0}OLZbJQl5rIJGyL;bfO zV1g+Ozm336Yy#Ga5YbDpL(YrysFxi49Arg*|LS{~Y(bjFolIZ@=m|~QT@liMhYI>uY9`yWRZQOD|FuY zE~g&=>J6!i62(`*YOls(tXFCWqAS~e_#)x8S(!^TZ*R=4#9lAK)p|ahlSi_emxyse zJUC#hTB`feJp0K_yH%EJ8o;lMNbEu8)(FDqq;<2-nc6%Kz~{9(ir-tjLY2TtSUQlj z^}%($tRvi#xH;EgwG`RlI&gqdTFp}ceUJcv4|C63$y=@9oYW29w$e~PfbV*=Pb_#s z#BI?S<2bF`?gaxs8^}2{dfjg|e>QTeX*=3{7?;n}>%BI4{PW$5$H*~P>kpT2?Mv`* z{G5E9_*@#?xVCMz0oEw*8DIo>Xw=EB&+$bSP2Ete{rJJRGOkf$ifUS6Tg`E%Y0tnY z{@xm_FP#6Hbq=}r^s;@X7A#cHM6ZiANlhS;aQ<|70M9WLWawROxa#`^#$dcZ2 zs#bD2%YRYe&T+0kZpY`qU}+qM7+4*-W-ho7^bkET(`Zk@Urk|HgI7Iv&Vfy4IeC@3 zBrE&~AYXl8y*YoP6QF3uF_R=OHVvF25=EOstLD}fRGE!UA67ZofpuibF@~WW`QGkt zTFFh+5bR48JuFmS6Ava0#RqiEa`LH-scKPu6OY$&k}uAw2ugqD10Y{|P+yQaci^XT}Ko%&DYKvN4vlm(UJa zR_)SDwO+t9ut8Cw-Oy6ailMSJE&zjm{6SpQ8#A7$x}I>|f-fWJWPiNIF~sTHOF)I@G`Y1D}>=qe$>88^}G7zGbWETEf^)L=W7a zb{&|}NDD?S4hD~r2M0%hpOFmSi*e~p$zcpgkHPIX8t+8dBnF&ds7kvPtV3c^zN^L$ zy)%Xr!uv9A-IIYDGXJKLdRjrrGla2rzM<95Jfu)6>UV_v2O2NbjQilRpbM~X8@EZf z#PC%*m^axh?_k}|P&@JZGFLzR0(vR%o;7XhSav+vAndepd^sj=VKiy@bh5(E@)Rcy z`KkWoV-xd>i;!o;?>Eom<2=DvG`pMv0}@#apY8f3|5pE{^s*t9~;gs=TH@Sa)2ql9NrMOW|TE19#Z#?m}+5cge`W(PFt8<|Ao=`G=(w+_yb`{bv zcs?|ZBIjP@Nk?vZ+)tb!jE7n;h%sFcE!z4$_se0tG&@Dor~LGWN7T3#8b;d$EFc8wRp-~>21wu;3^*YXq6h_UqmeJLMEbBs35 zo3Do7ulS(BL;5{OpgA7C=?(bEac?1%jBzqDjUXxjydn+$~9r z#xLszL@b(`o&b(%#(_{bf#_SI=2t~o5E7Ae-j9zFxMvvYq`EPPPGRJ6S}+yboU0(` zL6R#x*ZGzM`iO&>Mx5%r3@P8cU$L=$&`Q|iUD)zW8N)ShD-V@E0L0WvoL+#Er5wshsWM_<#gaEUI%dbG6A7Bl_Gr`+lbt(a@BoN~)4(&Z-DXEQ%;1Jeo`P|B<%})8N2RNvV zT%M5=_N9ppr+IugG}OcV4F!}6C zSAz(<2`LM|kJ{G)Y_19<*Mg+_SqZ3D6ee#VbWz0fAIJ*R3(`bq1dRFml_KSku}xtu zBnVD;?VJN?;XQ~VG}guBmdoI__US7=vl;P3{({&!Zj&GdBCbv-e6IH8FaMpX5POQ% zLgq*$Ka--&ZfRIEowl|D_h~30uLD#NFkF0W;Qf3}1yKFOV8?AW`-qSdU+E$DA* zbuLN$#aSgGGIH>Kor+8r<4n!1Ma9|~snw^m?e;vULt^2ZWQ23HGJU9LbH{dMA zzDdDdTCG8Z`6x(|O*0;$Kt}MxPJgiLKid{hSu6m9iL^y;IH@LePYYY6ie5gdKfTCr z?8|=6rCW{&Vmk#Y<%e=OiOJU)+~e=ny-cERsh9&Vx??RpJ3Xm4G`G!l^(%7p(U4@J zxkXB?GHLi+Sd4SHQl66t-D>Im;9PB zb#TOOqpETHk7Ot(m;q;`+%G9_K7uF+I|`PvU>eE>e%%3FQSr7U1(XE@%z8Oon?oK9 zUEdXE+wKrvrP|_ogDu|yJZdLA(5cEsZ{lFDX}0cSze4R`*hsH#8-2&;KDB9v2subL6-a87VEmUBgcXj3h*PtGU%DI{p%;&y{r`78rx|s zjXiO!I(e$E2{VhZuF{t%BZLpLi{b_f&*5`0&7P=6#p_5+PdwD{-#0vfx#62s-T0pl{)p247c|h=uhQs6<+_l4rYdidefHF5 zg!j+Q8xSXc0|6C#U{2HOfJ-cJabZi|EnY^8Ch~Zd1Je2f3M(h891+WiJD6 zZYi;pBM|Nw@*0iNLg(tu7;-z(1T62WGe&k9*Qz48;3QlYZ>bp6DdhJ`~G!m@h{gi z(~sL%C;#;lo7=FIx=-Ny)~Kpf2~B&L6k=O()Jv_y={n%QsiI}J!vuAx*wBWe{@fH;ck&o17SwF&q5T(^KyML zmVex@Q76%}6h0NBzkh8AbBU{FHUpvfE+w!0YH6(*7punRQB)rWzAi(6@P1_q(M>l2@2$q5p|i@^N%dx3_9 z_058ge|rIBABpB=)jNCP>4aSVTyAfundHHEM}Yq%@UMyyZg6^Ojs3#`+()UAn&6lv z2kn&5DqnNYc;!!1^B^=EEF$~&9(ysN6E~=2?f_QmYmbCRyPsepD+=JJdzW~+?;uN- zSg33BA9O+0rVThC!?hmvKxypDhPKcW^9L-;D#?N~?>7`InwofXg1^BdnUwx&(T1;G z<7%x9=6@j{ARWw#uGsiPr+QpnFYBtgZ~yCJao#?|R=_zqDA>VVe7rMcsnQnI7k}QFJJG?nIf)ro2N|kzL=)EttVywkBezy(fpYic)?|GThIRQx4nBOXgx9<94lkhda-|b zYMnM1O(3>pIv{b0utB_poMhd(fa@( zz?!*W0`;#?_jkXf;}l;7e6I{(-mYXJLe~xyzU7&}MhlJFCEjH$0J}S(`%?R_Qbe6I zFk2?!LLv}TjeR?egD*fZ8w&>IPNuJ9wZ|f$y2TH~4gzX#Zk07~6qz;5NH2mrsc0}W z26IA;&qH5rPM{j{40!hf>9oLO7|RThrZ||D7*&_pO_b+}0dG8^QGJ?-?DVok8A>|ZXOD~)&UodcJp6y+7+_Ia?tCI&DG z6TA&)u06EOMqgyT-9cbdDj4|KaJDCdl2ZPZR>HGr-YW^bXg%vdC-kVMdH(HxlRv`x zu+z3Vw4h6w?5>#-?R@V%{<@WMDq|<30Q=YYL2_@)9od9ShZQZZdMbuaTGIiuPZNkA7O*j4e zG;9%3JC$JeOW{K>0Z|StSO8lz8}Qy}5ucF2Z|#2Z5%wU6zj*`f)%^o%Lj7jrK$G8t zMz#<=K3JcqD#l2+XGD4RE&rY;CbU05k2IGKLFrVy=bg)X5){p1j|I_6H;DZ01o1LwpaolP4bbEp5uR^j)NQ~wHTqZ40qAP2X;!Ad@Jrlt z-4@DnEcaHhY7yRgjB)5^eEO5w2%)0oul4YcPVHqf9d{OF!?Am-(MR)F{sZe2(91#wTBDk+*K$LOb;4;_l0sd!q%2ELqZV z0El|pmVE_?}g-Uq(@b@BCr4x#&`v@gV5)nFMvX|jFNhXVGtU8N6`XX zne8W5g4t`JYCT{A8{%W&B$r(X)ldK;fK-VQUKOSe_`C%Mi`1duqOu4CPm8dCnDixC zB)Q8N>rI++n%{r--+|@hS&@Nb_|=&K~jcW^3~z?uOc1^|~uI1*T-f59Jesxd$zvyJ*HtvxfV5jVQw z!cTwyEd2>Y#y8#<;)~ub0=|s>C}D35b%hJf5`FXhwLtvJYcGALSdyI^QBy+z5Tf{` ztaXYKX_vv0dzkC1ZDsB8You%yG7)F+69gFcYliCN1KmmK6tYLYladeU2{cnp2VToT zl_;X$tGb6(Q2d#TfLU=t?qu7F3L`u=+3rKSCVyF2K8ogpIf}~|@s^XtDZm{Bz$P3$ zL4Q$u337ZYnbH^*6ajGKSObhfSWN}$wm@2hAZQP2JUEaY>44yk(P63?0BQ5wkQ!el zFF9a3RM>gPi7nH#+_`P~1^$>0{;7^V^g09vugAcYV3QIBgZI)7!JXA^aC2Qwr$7V> zLYr>)Jy2q>){xk!;BO;J+z0!XeXm%?p#$NcdO~hO^$N-B*9z#rea|F9e7o~ zXB+@;tP1j>NW;0FuQ`NV9_3^qjx%2-e*YTnnK9ZVJ}8OGXO^6i@3PolC4FLO z6(848X#40R=hF;%1mfMcB z$7@bENFcYIzz7$CmeS%B;wW~u-LmmnovC*N4i57^r@D>D_haI%0mk`=JOHG)l$y2E z@xiZDbB;xjS{)~BuJnD9Nbop`BfrAv+R1z%*7pkzh*5`jdI4B*onz<`#72NRDvlO_Ui`l6Wf4-(D{La83F=h;>-s{&=JFZ$ z#@rUs{}Y4u?*cwIwGosB&L#CHzcj~#x3hf@H(lziV+dXA1RXD9bVAPS zM^ivkV#Vk^cU56J;9EU->i>{$Vg?z&t)p+2p@dcI(eCRJjs!V63?C_(0#t$3>7B8+ zT0%e9v9q6nBP*$itvQ8ej()y-{;_=BJmqR>%Nbo$)Fv2u;2i!zK<{*Tixp-i8M%HfV8Z=V%WL0ekD)OG zz*$6Rgxh@I@3B4q?o41osxR`=Lp)RTGA*j)l;sPkQY8jkiaPVGxA_9{uo9TA2KiR*#m(E`XfSe1dN0 z-T(!3>4q9Quw5eKHsa|HmRup<6Ax>#006TdtN|^CO;~`F*%xcTO6xPUNw=Pr`)#)Q z6*fw{=b((%1*v`BLdZE;37^a-Bgy`HHm^Q#3tCP z>^Npgm*a_XsA1?^IZ^mfEHTjtfbQI&CqVj`FT7ECs`VZA6H@?-%{{wDICg{Q3e^*wB05n4)JoHmDI8Co?9( zIK;frqAePzu^rnDgWX-K(XKHrPGWF&3;~R>C3oXnw!((^CO1EeaV^8?k6ES(>sGOJ z16$BrRFPMM^s#6z{gSk=j}fBkPlg}UtDu}XDt4z5Qr9Z*(ZMNJ{+XVJ{*r%4OaCC}Ul{@!KUSII7<0 zgJyPU;W`*dH(Rq3>`KCL-Q-kRPZlvnLK`94EO{Z)4J3yf3}O!iXy61Za!@rlg&)jq zmIGwYsEoxQhq@F^*iIu_4+BH!PhVYs|DN!f(Q+Kem?<$vAKe^m^zZPZRs9XuCyo#k z4c{h4^16NL0M40~iqW|^zH7zRMaix}rL2csjJv<`(q(un| z${Yv7_hk&n7}@Wh%T_FcvJ=XDQ?_*>c;prYuLP7vJPxuHlzdXq;Oj%m%yUs30kVky{1dIx3SAn+hI>>hjYvbxG${_EA)2(`s=624atU zlOMI|L?)#@>#7Mu(mMjr%Y;If7D0yRNK+O#2iFwzG0J=>nc|7M7MSP^;K~tiDyti; zSR-F3v$jiZhdnM;G@eTf8)xETlqGydj@wiT0)p8?S}3_9kR0+lh{^KxyQcg3>s6_z z8_oM_hf?`(AU81_H6tm@@1O^8&ZPE|Mwmp_0cxk^mek@jXtu2t9ZJ;2lPXpKPp4X! zg~kUQOG3Hr3lrvqD1do43y7w#ywIiY7vWV-dOe(wzH)oC>WAW`f-rbckw!jV$CeC> zGQk3fJI_fWRv>yrCH02T=-P8H`kV*j&sq*IH3F&MTV zq%XEU{n9~>cbRd#)sKktDA9bSnDus?mb}2w`j0LapD^gxsO#pZEBJ)icXa+VuKnZh zAYf`(Q^Bt&8=THYi;k_o8dAeU!BkIAyR3P}kg@t5H7u_al+?Byg42~O@4E^qv?{J1 zrrMUss1q-so0epP?1|Rk-Y9Q?=C5-*iKyFxv`YPDz$_@YprWTzo+j&i(Ei%mf=NzA zFG%xhf^G%geS|6)E>;GUYcOB154NgU-sNS9TDVv_(=4wTqjzJb+=KU!XuNcC#l37+qPbTLx$-eH<|IoNPP#)l8j;auh53-GMvwO_6Q|GdAv62VKWhh41qw zkf#1<(j)^~C}zWhE`kn-mBj~c{8ppS_x98xDv5fP2$+FYC9We1-S?zXlL@@fE0B_w zJFN=5UvG4rHfz`0P@wlt5*taPogvn)g)f)k2(`F$bK$*^&uzx zR)sasX!{pl3F@8;R~D~xHL)m?gGXW75yYwo!Og?&I^5*!NaZQr9{XSR`)Kpp78xRNz57q+=l)!2~b; z-ksO$*rra;ih$EH(B#xh0loL+y*b9DSE#ep1<#KLY|wJL}7^? zjEntX2aB14m@8GpQ4nR&lwrrJU4v zLSdT^Re0W{H%5i1Y;>M39+GIiJ&x0x5Hs_P>*G7;+P&eI@XD8pwI_G)6vz^v)3rc< zSpU#msB>jLQ1)_|t{r`^mTNlsGSzOvZ^x=)t@&ypaDWN74u~|syfts8&PgJ~sUemN zdBwT(=L31Yy-^on^ECXbW?c8V&Pwv8$z#y{6dN&dajOisOGGyw+koczdo08zuk&I> z$gN<6%rTJ! zmnZl2R{HubC?=?6h*B~$Zj~F?*4NqR)gxNPAgrtRBU#+!OPjoxjNkZCt=7&M?8jqq zKNLr#b=s0(3AHP7dORF^9g?;E%m3?DErBQTODhfh%?)!@Te85Yor|+U;pY@FS zoQ4UJlzX1a|0p;`+~XtV?!rj8As^D*<|daAX#~FD&>s9S*J>&y zv-@JX*EM{)^uE`a2}lvM=&70aP@q1BVS#bB z+ynEIx4^1p6yu!aNkS}-(F1Fu1Dz7T+SDhLSGm%3Khrb&>tpzDPsoCF{LYcRx5e7` z1`Emwt}3tfae8m&-&C0x*d;(J6K zqTusO9(CGM4SY1J?=P7d-LNu1^{MD#AN`$uc9c8eV&#O<b$#$DKn&+0nue;IlGx5tZsaR=EZzQNG>Gz7pv z;B$eZ^7Sw^S>X5l?t8)%oxeV_Oq*KelhNk)@gLW6VtDZzR>mloMF3;%iSpG58XWzM zw52ri=TKq>;COTp0%U{Mk>r>GAP>D9zdNV}QUPkQGByRRHVJt6&I$o>;pxjxubIwd z;R~s6Y&{1n#vzcM?Ts6C)UCC^4%#1};;b%+fo}T)%dCsd8#3ZGEN{KZqPa|~kozn6gdp!U z$n&-Rzb)SXYn$-LhY|!=m(fTX*G)XNB;Lxz;K7mxMASyX^mvldCJ#ITwauaIFVG7b z%GKzl(>#08WynS44@Ap!%?AnHyFE#K)g7*qETNs8x*uQF07tIkz4$3nKv<}LIQORB zoKyF4jnCjF*c(igOUF4zIH=|JfR<;OYKe_GI(CkluXg#tLFd%hEIFM$v z=92Y8PwE7VK0AXmkhZGr zB-k6KhfzIbf56Rt78o!zh#|i*Mu%L@^bXP{ElAuI2eZZGQaoV3A&X+PXCvjHL{sM| ziqp(%2GbET1?EWY`k{zxWZm^NLjso33CQ3Ru=!b-;;(w{io52>u({m>Se8I!J7nmQ zHQVHPm1C+8i2P{LzR5a1mjzI_Va7UOBba)^S;X1h&H6%;%n8_Ty0v)dot52`u%ykQ zTmV#>Nb$#$=}jcNVg!RX9ygW<(1h0UyUOgNj4StHGsX*CeD|JXsoJP&Z)#36};+;^c_AtUd7FO`W4O z=h;`3a?VMpGYo1DW{GA37O>!2J)oZ%p1P z!T@~P1|5!wXU$6-LEbmtCR+nhgh*Qq`3jtaR9|VS955{&_os;^q!#-s12I_h1|WTy zz`Hu{veWzi{zL%0+9);-qxHzmhp<85nTS47+2k_5sp`(Un^ z40oZ8o+zS7+wRvqHidV$MS#PsdcMo*7uzXdgDAAIVmb|SJ847N#RfYHhWEePZgh^6 zMiNE7LmMdL-Kgzq+x4OMcbRKKXPKE{ZD9Sr8*pyY}>|TKg7Zq zlH77!t^KNVKxf7LB$c%HWDBKy5g7;Uq_B+GoQx_sCM&^V*s~0X$Zq)>Pkn%qVW|HZ zm~9J78kHeF>h!xEg9Mw&J*qa^vHLH^KtIk4I#GZLIpR&nvW(_Z@~kukn)) z92ZrK_PT-}c#5xmpES1ylOK zKtkRPxRs><2yt96ue1srAjlfau;x5$l$U&aFTV(|S4aJ{T1)Oa!?7ufnJRyy{`>G#jUY~*vV zBtAjcm3Iv+lM6K3AfH!MO*TBIcv7M)5+hlHen zl)$1p7G083N(&+?-Q6wSy^xYdr1QRe&TBxKA;YH!&LW36~lHI=K$Ya%t#FM(|4d zc6abY_KAN|!~f+I{EyNE-yt;Sjg@y};&m`Ef2{fve}F)86g zkJ8bzJ-4M)WAirRDMA@h&fv(op?DI|;=YSu<&cD0xFEY>DL+}Px zdlMcCY+Rf=ALZ{nq2$4r!pkTI{`GoD_pG|F#!Y>%F(G?kzxRIs0kU+E#1V1OI|9+8 z*z2LAYRUUg?i&yI+iPtI)mL-jK!aE^E?a+oz~I@~w#*GPDQ zdM*CNIM_;?bbiHL^*q_3sHD{Y?(RnK7>V1ZpdSX?mwXc3(}iVwRi^unN908f#%~() z9U1b@Zh}Q0OwI0S>TP>vYw9;58~OtK95TuyT{7CN`~O8Dk@<}5eCfeY!3V>YCJ(+x zF+!hdsRr8okFI#3^kl(4XoYh+s>vX?h!^A+Rb{5tg@EiL{tbcNl#e|5gL$6+E69Qg z4(k8dod>MG^T;z-hE#bN6CaYE!=4KazuxAOq)1U>?nZl5PDw@?fTT)4j<{nE?wX9nEVlf2} zdU9_H0V$c{ci5ZSJ34%%^mh+6W~fvTF;`hXrUMc2QWzZeNm6xP*ogc5-90$gHD{|zv#6xCOG6TH1Ynish{ zhDi+EH3s4`7|($@6%2M2!)?hWn?at`&|yY9_<4c2a4+5h0)NZsfZ#M7q3#BL+9 zL^;L4jG^-^&A$vN$p*5@+0sW!AD(c!trU7ubl}#UN~BdR)=(*w21ReA+iNKYQ#mG|Z#Mz>!sbb2vfZ?Tg z2xv-mZ)x^lK#gDvh{hq*z0;a*gea+SRjU9e{iJsdvaI(`x7CD}S*x2=L;IBM8gJIPh%ge|CmzNAvi~Wol zkUAX3&8-{Js|^*WYs6tP&$&iD;P^Khx2?kdPL%HHy@!%x-ussPnJV*!G4H3OyW~?iIGzX-b zt~0F~xs3wz3`X5%`{+9Zz4mq6Z}4dHv0FgJ@*+W%tJw{>B%zfH^ z9O!x=hPfdThWzdfXKEa@$CbjmPs|67o-`HNo?^a-z*+(lG>;B; zpN~&J7|nQAX*tytxZ0O4i%rT#G!fGEJ60kE?82&)j2_f34Sge-*k~IkWl#7Q&)0uH zbS-~m?%6TJ%3$|lxuy1gU`MHc|9JaYP8_ooLM2ZrfG_?Dr#Yl;Fc=38pu;|}i#%$I z-HUjg4Qr*@bPX5}1aovEFeuqw)I4NGyyyN8b=$Ov342zJE3h$EJz7fVgM->+GhsaAJJFiIf* zhmV5>ND9D=0S8ZsfXpfYVF%Z=XDTT&?T!x6gkeJe7^@5OqO0Fy0Og1^m5*;u-b^GU zu5w6@;&YjTRkkXaUl_Kzr}trWu`Fr~ceGA~$Q`}zk!&;-(L=V}hd3Cn^JrMH5{wVI zP1iYe8$I9kHKwpB24qLv7f=2+Riuyxb;y-~U&Tcs*T48r|5*uSLg_5kY$@vurph!S z7X?5e$)deeqLK2aiTfGi1ZRl(d=(B2-vNXNSw-2#5^ZP$0PoczNKE+2xDaN<^mMV} ze!jY(^^!Ox6n5Q#jGK*Y#NE>mu5;ObgD2W2gquo;zJa@H%gj)2BnEE03?4Fwtx4ug z7k>7x6-M$OE+)9z5M@;E?lKT1M#29!G( zQ{SVVhekfh=IgMGQdfQ%TYh3SDwEy{V_a6CLw8JQyyFVjY3y-q5&Kh;Z@i2m&mPOo z=3LbA+4VmLSxZ56F)_5_NhtvK+cFmOJd)c$#Z_7>*%1&aa#Kn1RPUmAa74R-I*4cV z%<}Th{!n`;flIXBrI1Pzj~ICUGlpMN5YZw1`wu_nJB7-Njlp!6EuP%n*=qYtDj{dN zMfVlF-6!v#+PvJATHRkUzP)>6>DY+ETEjQLxa&N8qX_)N6M@@G-xV6p+R-K-oAys) z`DFe=v%&ahluy1eZ;s_T&GD?h1h>8nK(vzhR3eN#iPJ1ECOO#r7May6Bh*RV&h(fc?S>?W74boIBQV5}V`vJ06{cSg-d znTBWhU+Ty-#U`c_d4+&pM!nfrG>?*Vr;L*yd^0sB+C-lTlO=U*hRe(`O+>3c7)ki^0XRC`YKOMs2qJHNl)3r6#`#Rac;P9LG( zcd2|SnLUTk*tEWd>heMg|Hh*g2 ziIedyQg71Zk6?G^<~i4u=^MY?{(Nf<2$|#owcx01C3C`Nvgdnh`5Gd--SP6o*2m7@ zC4IIsSEn>5y0dUfz<%-lh-p`!O2j~I`NnF&Me-AQ^`}kxhZEWZjPgVqm*tcQWUQcg ziDF-*4L_D|<&^(bhy6oQ)j!KI^(H(@K&D+gEn9|-835r1hZCp+C+(D)ga zRA#cGGP&M7F?zDwywyEOEo^>j-`opA1sN@hs9vVVa(drh6C9=!4T-4lNiZpATp1jk zttAgAlsqx?QSE)ruR8>Bsdzi_oZt4MQ!g}F+3{Aj~G~ungSM+3;h19Kc4xD%ITh^0=V;z)?-l5 zBXSC%w?Qda#b5m7u=Eb5f!4<3OIoxPF%iSU{*>Qzt|*)z5c3O9=$W$I5&HwQ!BNrmBS@6$?h;S zUHa)U4`qA$rSLY2!vu#L)}#kh$q #xfNbSS%X=rzbZkEUV*HKy8r9fZdI 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�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%0ALE;mS%m0w7^ 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�r*E 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

hg#uug zQoltiw2D-Z9VmsqI2N$s!9uKZj8Wh$)QVBiJUI8*1cu^*Zr|%5)hHXri|fz_8o8zg zj?!Pk*4;0)+`g(lAw|k!uv;?mm4tW|l?D|n)I}kU!BNM38=lKpUnbO7ebVV*Pw9V06+`07=-BY@^7qd>v9p_7{TD}r4zCyMFftx>>Tu*Pw9DWx9a^-qgH zDiJkmc+(jqE8(@`>IL3p$4_r=#UOn%Xl1169tAwH#i;e2Mlcu7LS97_Aa91^9V#1v zV0`vb%~m}6y6n|H$Q`Hz_kIBxqc1f}P_@UEE?h3+8(pkFqRtLOwr8H`Byoof0h14d zLkVw0c1};xPTo1LBb>Zip(e?!K>E^@(Ymhbm#e?wg%Xz#P%ER@pcmf6;FC)~j0{}6 zh`5xi(ejFXvVLm0g0)B4HxfdIJaYDs&`TzjHNGJjD9OU~e=U&p;k&R>v;%Wk^Zir;==n$62$!pnOhHODbAbD*(2$C$F`G14NA!z zAR3+m*Cy~`SW$%ILQ=pgD~-c*srBH))jKt2m5$lnXW1AX+dQIpO5UCqyWB=M5=f?o z_dh*wt%7DZ|F!)I-~VPdiD;A)_aY|$bI06tbBl6y{YF;*4CA) z;r`ir`(;dOT7vWV@X`=V-XhmAq~kMKWM-Bq7!tcy8?n?f424;$a?VWmyy!MJQB-qY z{Su72hT@87N&GA+kpY3ovxT_h^%;_(vYjYc3a>wZpChJLUAh*{D0?!5R~BEXU>8#Sd_v3bD0xn(-TFBk@l*gQ2Kn4aSE^_c)*(t}-v zC8N(`<+w1eF8?Eq21Lr$&~u*)dV_hc8+2@5Qx}D%Bq_EK5pLHK9SY20^|a%THh4EV z74dP+v*FO%Jy0VE!vid|3jC>WB+NJEV3v5NR)37U8!X^sYL0-e7T-PGfIjC)kXrk| z=`beju6DgE z3c2roBAcuxWDAhpYAVcn(!C?R{d(8NH4(*L6Q(vnZDQ_|#-3ZB^msxw2Wv2P?*Jyu z^)!DXlu__}f{9B~90iK1Tt48~0k_R~NgY)Uzs$S6FVU0UoVL{k1QOo{ENwEQ(50-A ztuQ3t@&e?PIQ&pay?1QyH_Sr2T)3Fl?jwN~qvZP;*PZwsw|o@O6pyjpm5kg*DftCe zroHcuojHtYPC`Ig;ino7xuPU4@(SJiOv%~mhZec;qrUBND>XH5z+pX@s7Ka2A3lNn zEp1>Nd!&*t!kF+&ssecrH63pP1vln#@DT0EmTuEDuV@lDC5xvp@fRx`s(KUK_sSGs zXzX`JV?S1GNs5w&!%4ZRg1)ObB)H>CAdA9c@_F!Un=N!1p_~^nc@w<_bms+Ls!%wF zR2Ce%A9~0ePsu46YGtc2u(pTJn=m@2@A72$O#GHpc{=&2{DW{4PH6)fYt(nBV=hW5 z;#}sp!SFm4x**y@ak&tyb(cCnX2BUR(bD1d5?_3|I_h2-{dLXBi<4Z>j|Lt7>G4{c zOMUSEP%hXSuxr^cY}OkHwzLMYUEz84KsSPde#G~vLmmo&^cmlLkYz9?KsfwpbK;(t zoL*A;)ZsQVRC32Ls=b-z05N&Vi$9xq885-OTSCbm*GRAS`^!yIY<&BDBi|in98_zX zT~m30CVvuK*G~hIXllpgV($Q4z<7X8R09zWy-d@?DZ{LVPwx02Z*|q(mIt@+`Zs-z zyP(?3(dY6Iz*vq*SG8=)G%*aH2;7dv(r1ZiY^+N+JUepAA>;0<{f|SL7l5zO>Os(P zHN}NDFqFagsf^3GMftE@n9H=mIRAkIW8EbSywjYfWSH-Te(5JO0e&ZA$WHmbjW?iJ z{WCSiL9+0Xe65h9u(C&j%K5)KL|pe%-Or-o9?prm!h&Aazw`589uz%@Jg!{ToJ*Qz zPQ`93)OqAAIP!#8E%7q09{zTeq6=)2t<~c3$r_@yB}G$r%A;nONjj(+{T6Pl);n-~ z3AQ6@+xqzCML<;4<+~E-p8J*b#6_kqgM8aLBDHNG}66vskBuQKJ zh7k(p`!}h?3v(!VMM%QU1$YQH2JpPt79C@xrJ}eoXG(DO{dN@6Am0g-$`8uU<2()^T6Vb_};;$gk~d{$g(|+DchAG z@kyk|YSr^EQV4#FL+PrSJ2lNINwUb1x|i|`N_VLXN`cHrc#ex}_0&!I-maaQt0nff zNG4P9X%SkSwdyb0ho>q5pe{Os0lRs=W!%?X=0gfRIS)1#Gvtmeqti~05$d$*-;ZR*D(h*C=RC5Ci66ceVtQ^j^dUF*?t=_?p*PWI7Y?q$SF(t;J2mbhlA`g>js-%C-a_X}1|bFK0!>^xAVT8 z3o3PQ6H@w0OY-^6c>D>KJZ6m1_aX%pL+mIR;a0~ZmOnd%Tn2ca|ErzJ;bU-fqAka& z?;b8#NdR?ZQzwH$5%EPuwd&gH{&xh;pd_e&asIefLvuw?xb?x1@Zy zrXPR)Qv>`Aw3_9X!ryKL^79l~;OjpG+K7YAC=dlw)&9L&w#ctNWol^nuSSM3zX0jW zrn*-GKELrukE7U%=B;@RhL+Hbys%l!EU56`0iullz3$F~`8OF?hrf(dJrI;Y5n|XH zdvE^#rIa8ieuVe;`n3{QTxO(-Ejg=!s8R5NaLMzhvj2TAkjAAQ*Ca1lQ^Nl85`RHf z+RHpz$G>+SRzddA79W@pulMhogF)1{E=q;tT=n|*F4m}>QaUBH7&mzD$8evWJO6ij zkq^sDOhPGVxxGA^K&rKX`g_a%ZGRxG0P_&pvFxKx-#ep%INUMF)Dof-!e_sf2_iQ( zF$Gwzv3oRC{zCV`PI}^p=eG|?ecc8t9~Ve3!i=*GWB%SC5^O#^2bX|zB5N-6;Rm-3 zWXDGa?ZB2^SD3XZIeqy&$*`g~$cg+>BUP?Nb`u{>+T$!J)qmw%wnb}05r6JxhGH%^ zhr7(~Qc3Tw<(M19q96-wDGdzUsa2m?cQXn1!utEIb|CfHz-cIYbi+PGCn0l0IHMJeYl; znQ=M@O`G%(cvgdSp8;wqD9rGE+Ax;zeLA^F;f6bDMn+Ts;e$RX_4>Vk@D-28AUQsp zKff9c6`Cwc=l@g5l5*Q4+DYK9W`V2=rl$$*uuI~Cc=dQF`~s`%p@)(1cwtagE#ZQo z^TJ>hRN)tO6ApKJDN7oifvWmai>zoBQgG~Qx`3M#q+4i=eRJ#WX^8Do`w{KbEdl7< zZ?vOi<1;YC)db--`&F5Hy=aL)-(ryBwOj@!o+Wd8%RqJ-EUyBUqtQ0H$dij&jTv;w|d zk>V7JI)>hwCvW7GKV#Oa&b zNN3-(TYKSNRB`mgKBC*nhPyldB%_e&*n(G{pD4QB0Ep`WD`%=3FqyzhmZ*eg%#uX# zB8XPLf!Zn_MoPmQkq_)ZJ@`x7pP?tOg59WYZ%vJAmB8UVcvBbv$LF-OFfZ1v+GC>- zLDlLp1(-E}s;=UeTsf1Fsa;Ri2d7aGr&TUTEaIH#Np6N4%3da!3%BaneY(HX2C0Up zAts4piaMluU@{*HH^+hNV#5_b-CZx7d9ktAN+WXulUTBDlJECrp=W_?p2NL9X#IoGBF1CK+ z4kNlr50pC^Ks@Q^n)8wYgM*@`XXymT9RiOq@i(%cpK84h>&~wpdj&u77u$Z_?gI5A zA$(xte9&8-dtr0T(VYSb|1ctz#6F3%*t*RaY|cRScX*ntKkX{met}ua%(i&ZS9QC= zXazXULJ$-c1yed4P3x1x7q*4PFRBy8@{cQ1`y{onzGajo{r$6!br-=RNDyDLQ3*fA z@WQ1>?M5W}UE~rJXysIek=pORgk4SX1U;6XFOl1K$s;xVEl2q1{6O&hX#tm)30GUs zbh2>k(fl^_7Wachp4{M^XHrZGvTymj+JwsDxlzvCRQBD;3&8@ds{}f+A-B8xpn@AW z*zqlHXJ%(GG_94huhEGa-?;EFXFJhrfs+Suw5g71liZ$VKv1l#%ag9?~Or2w1O&$>q3d#5c@VB1)tomhwTBX}^X!S#{yw zp$_M`CZm-E4;`_H)Miq1-cLi4wT;PcU;Tp#uisPTRk!(#CE3m_F-@>48hQr{yI`EO zYI|2ch*zQPq-ytsc5i{9WeT{6TRp;CA))OXUT&q#ak{!soo5$o=gjL(gMNbSOD9;P41*$D{cHqf{&4e~W5 z*xBGT_FE*hjiT-DeX{q&l3)kU*lH zzD=n#?7YwflO+$ewNx+}OPDm}(RgeoM`2s&+J49xT!TNQXrMqp`5a5PnzSLp;5g~? z(py$HlMzSk9eLGvp}JsvV|?F0V0FE`_1Zb~BM84$-wggt807Mg2w?wV9YQl-hH%!& zxLfH%FF6zL-z9Ky7*God-Lcl+x~R@7Jetu#Xy5(INOw> zR)(Y=fNupu5w{ojR?|Bg`3Hdsrh8rC30F;|ZH}>9aNX_>bnI1uk0;1w?Q?9Lp9wn! z`n)ooWCG)Fxo@qPj5(cmgc-(mPA72HDdd=KbJ9=*ugb0;y_pLtP_PweBO+ z+0(cpALu5*&H63QVVwxG0>7XAP0U5CaqHy_5&8LEp~uA$7pt7c1=sjexU2{XLa+AM z*6FcNI}L8jSE~kJpd+nr2b0#j1bU|!7q*aDw1ulq{P29{qS?cV?*3*|)m_@jsmrv_ zR*rDAyXE~uTP;yEJtJIwWOtVb)fC<281q$E%IpAlIg)bHgzxrM8S02 z^V7QL&(naO`Z3>t<=tg247a?d`J-TyI$E72{7f>3n!D!zYJ%TPK8NJ5$e%2vv6O~pd_+rIw?JfMeR diff --git a/docs/resources/cloud-ops-metrics-explorer.png b/docs/resources/cloud-ops-metrics-explorer.png index df249385c66c8205ed5211e99566f9f330f96ae4..470f3fe2b7a959a5849ce5858ca4152593b3c061 100644 GIT binary patch literal 261231 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%0A$vvS>9-L(o7OZ_(1Daq|0Buf2=9xJjNT6F*S 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 literal 69562 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�)s-~8j??C?j-9!6;>-v&pS*Jl3m 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


e_HkLQxJo|bs$`~T_I&cZg%Vg!gu5MU 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ⅆ`ifqE&q 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≦5PRdWy3yfBu1ZlIG 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 K{hWIbp8h|?9$Ccz literal 40403 zcmYhDcT`hL)bTLsLLO zx)hNv2-4x5-1mFe`u@1fwL|!|?Y?LAR2kL=QzYQrLW?zFq;1CT}6}@K`n;B$B`eTQeh8Tfd25~)vc!5p-^3+ISea-FTSp zHxbAgx1RZEr}o1{U`6erz(LUEVbCRC(jdWOXbqA~xC7l86m))&>@?S69C*CKTx3vY zLYC!Fp9a%(3AwyDY{#_u`~G;oQD$3GRyRtLgwQWurXKE~4?Oxw^5e;e49)#KNyqPf zFKM~+JXVJ>MHrr~AVQKW2{9r~D1<>NKef_P_C?)NtURuw{Q@!VT%U1vemKAP!!D~o z?P;5Ud6TD(pTfbouKQYkvgX=ozNGSd^|&O$D>BISZzX(_u9xSB$?hw-{!-(bAFq`mr{CngRlFl)T334O>6}^6 z>HgRQ+n=2gqd9j-&gBpHT4uec@Bfvz>7!ZuQS+fK?UwhZ(GQOi#eBD=o_7`Rw+4!_;1pSQSd*=ajP+&nv4iW7f4$P&Rof>G!2O4zKOu-Kb&tXK;za-OcM zw(2GiNM@F^C#h&VDZc&Pt(Su@=;HW;R=SK%NR{nC61jdy3L#%PtZimKl8UXsmnfDU zRqOCg^KYI1vAZu=={)P+Sn?N0R+?vEO)lA(5!n`B-k@c_d)oH`K9o}DT zKuUDp-_=Nya?S(0;@!M}Zb4y2@U5x06HLUIM_b29qBY1aAxk)86}e#c<}Z1aPGZMk zx~vC-E944q5E8P|6QY-h(6<4PRs6~5jfgaHTO8{ANrs@%$M#OdXaj1OvO(Siq1Te zCc#M94JWWlJhpEOmSGSW{{SXVw$A43?(iLK9oxO-{%Y?n6aBuUio36`l4EXU$0%4* z?6Ha-SKPVgx0>O@KwLlTZTjSEuG-gg3+(lIVhrq7ijmF`fr?275`o&7s<~$savLJB zNq7XoKrn7(5Z7|?o!WhCv#_ebzshfYpm2WO-MOeyA_ZBWoI}=@$WyCE<6|nr%@^5@;BM8wyDL zP?N1h<^=0q(PBFiwC+a1E=Cz1TEY`^KW)0#1RKNaV3c{Kw&C9hz~YysF8+8q0JOi|BKRQ_Z2uyRLITekdZ)dALAjBy=^L8oe|YzNEEr0-KR(VK zD|6kdpnCHim}?Z+FSc9U|L=m!>TLvsMHHef=*+;ACyM_J|oX_pEVYQL?XY~ocIgC!H#>VQAX!r_A zp^@yebz4+0IkuX?nSzKIlLBMrELRw9J)9G4eSR>i41((frru>%Sm^N2XX*{+cf$A? zef=05qIYYfjK-aV2$h3;%_>pQ=dT^%ez|bIRX6RT8-XwoS3{f#-$Y}Z7D6eOQ|&T! z1Z$msVCMY)4qcDo@EX@SsdacNcX{?#KV$Ju#G`xu$oMlUr%5!3S&<#pk?xTtZ!3IE zaStd4Ow(JiG;kO_Pb9z5<>I> zrD~8W>-~IL2{EE=4Snud9}v0@cFs=^la&Ha&IXDPHYSo)!iY7-3hvi9?9K;WnzvIb z`tI2nH+dF$6VJr?Z8v`SW!)XkSl!F%*C)Tz!rk=@mZ+_;tNOz>+58rIKI_*|8ejc< zpx;;JWYLEf4&RD-Rx|y|@j*_-wm&mXg+f7Tn}k))<^CX_RI#bbi~Zu%PxZEkI>eYC zwe_vSz7#fB^UfT`C#$R_mDUT=Tt>BIVK@rYT|EKayf+oSl@_f0sDR^@WK({@5v9}8 z^niaq94LY)6X%imqiK7#*{<6q=<>Wi-FxcE9+)W#JRdz;zl-BlTM!P!15P}P3@a@7 zk>q_#GEsu%4Y$2#+|-S09>0FYCas+$Y|4v|yXl^z8pc)0tv3QFJh$5Q9*fR_ceRad zOS=Y%W9qwwf@+CWDi}Pr3ei*0#~~3b>y)!pA)yTUY68c<(w=@?;E?y8x&1_TuaA4> zGqv(7M}}B5GGof2q>MmvM!0_FdYr=EOChrc^fx%2tJ*8(=2%f~(Rl>?tcU@C24}@RaRhd|bl`efjD(n!%}+?6|WOr<}A=-mG1q zoq=FP=%H)3+D^CW&0O+|^-FNzk0Rw`!VlZbf-`rQdIzpWvgcNQuXw2J3(g>$rJmQL z1^3zXlT&V4HU3)+8GkK3$4|6TVOp;}7jzjQ3sMBH+U0EQ75sB${t*ab_uICB(_+E5 z8pYr^F`=HZ6EFg=e7?)W9;%Z&8{f-x+hsfCe>-LcT{KoM)G)YE*ihurseldMFpbi1 zXO=;I4Q>VpM1s0yw(s9xCqC^PqB<(*f|gwa9)XXC7NLEDAbokDup92VJ}$6reJ+OC zyt~}@;_=Ws0ZQk|N|ljrxwAi?V>)OP?U(!F)wTzpwx1g~d@n6p=njZeIw>>@IQaJ5 zQecO;5^~il(+t}tePGTc;p@d9R;1kx)k^n88JzppKP>zMJJ(g`DwsP9*1>tiY3^ zK1t#fGxvU;tC!spF@#0u|E3?zMs`UzdJc;9_H8%sWYXl3hN^E}zq`JY{4nWZ;IW#; z^EDn=vyBS*3T0ySEuU>OecZ2+EH0lI7XwpU&h|kh;sAG_2*cl{eFsQtYsHJg-H;MuOazV(m3DtzRNS797F%wd+Tw_Qeg_%a2CC9bhTyGK68^2E|}C!{MBZ%{+IZ&YYQ<3U!f9uP^Z=_{{Uy^ z*KcyOWx|#tZwCHC;|$9a;dqlMSGAY?JdTLI;7ulIGKLhUPf_6$>ZQ;qe3t}=MRB_ zkoL2z>4SlzP=X-|bm>m}W#H?uQuF3I0+t|>*q2|~6`y|`%v3aeIA5s4eZB*N5mR<_ zCDdC^mw0%|dN(ZkN@cC{w1N4tP=`==9S9fE^<9|!8CnvT!%S)Es>Sz5ifnK-`J4dA zTu~kv{Jo{{Cl&O3+6iG-jniXW=)lbdcu$79u`-&w;9vdQ% z9c68HR<3xWkmd*xqQl(^QeU&>CPmzYsCF7GzwKtA7kG4~5F^wxY4%x1aL!$94(o#p zS7h;N$m}|T$~?cr?>>S1eEbWJL?j_1)>Zd7d-~_;Gh8@tp>iz6>%Y$F!q^uw)u8ed zRo4P|zUs&iDhHFq1XuDdh6?CjN9G2Nw#$PTs&t5j-kls1HS_#OC_f_2@(&0nC_|sQ z@Vw!U9v7u{$GA@vK39WRyctKokSf?_-yPn~0MS7KHc>6q=_h9r>tApm-CdM~S zQd-QmY7zr(M5;X}7Yadg#^1RtqHx4(Z`19^1aI`?yJePTU9&Dg8 zCo@ zWgkfi5wR5}L(c1#QW)HQ;q|H*k_`p>CK(e{9-_A^744&@phlkm5*p9Kr+1w$JFHqY zYiXpRboN@Eg&KL~BdF>oLjY?&3C*6UC8(Y9AraLFbACH((qr3A8zNpYrz+%LlxnQf z-2IIw%%T>hB5I;2F_D|!t5EOT%OAE|w5K4Ic^>ZD?fu5BAj!Sbhs0i86Ujsb3@{C6 zsDvccWS?4A9lr1Wn5YU_*&bp*RTZk!nlyWt-6m-MdaR5_9;as6?`&@W66E+OiR3!y z<4|}_juY`J&r$wTe>7Wx607(^95ua^dZQz&Ej%*3>pIjnk)>lk@brEwAOD92<|z8S ztm|@Lwi_d5ll*0Ql4J+B6nsW1)ftA<0Bj3oS)u|YLagC_hW2@=sb#WRt{4(P@ggKcIT2GAm`A&vZfluxDmPdtJO&N+d=99F% zab@;dZYP1kz8J&R6X>Vau7d3E;=d}fkN(`HvWgEMq)wrk?&{c{Wx4Z%Wg*q+Wn!+s z({K3QeqnFdrN#!{Soovb0Y9BgS*viRe!DD)yeNuRHMWH2AgX0o7$a4Aa}CXF#i`Rz zim=P+onY8*J9QBzEKZq9O5K&=xs{DA7yZ~YIkWM|i$p}r$MTz>_+b8;$6Cs|~ZG?ry zDX{I(VSD{GwYXgr6to6I_H6?R_ZBmB*6LYpt<$r)FibrRPCctL1crHZ-Rj4)&l|(} zpYmnD!TyC}ZRnx-w^E*k_|r%*vhFP0<0bMYk*$94ueIBp|>t3r@_32x<6YH^C~#MLJ0~2 zJpl!7p}7#=r!CZ%!~QVOui8=6dDIONs&Sl((OnP_Spx|cAUyn9B4SV7g=4PH%w5TA z5z{ZczZ~gRJ5AS{f-;2OZfiB-lOey%F|{p)qpT3DV0^ zqdHIYF`fTStRwXo!X}8AcmI1QXaLgDKn0O$D#~2m^2gg}!1x-z?)Ru2f(+g6qFPi) z?T67o^-^w{Rn|-M(l%zzpZ9VedGx4$(wu2$S>$M$_FI=5AzDmHkG^L%5HuXIT5g9~ z3Q$O!H>&mc&PG^&uYrwa<}-U7#_YJ}z^^SVA&l1S;YYG5N;Z~1)nBf(!eOKfj8Fw5 z`}d2L!3!3aBq*PLYD`1-Go_Bj*J8QO1gt^^W_u(b-gho?az6;1d~_)IXK7Y4_G-^< zD9hh4220gO*VTA*rK{kg_JMNRMT92Ra@8b?B(IXOve!r@b7q%UD2V`8q`Ip!T(;_S zXj`KD!*7t_Q6&Wat-&RtZ+rCb^iBQ=izct`1ue?!X^KXP;;A%*5%SkIqx!=xUk0f< z-#*vemo2MI=9@Sk@WFNphURLdi&BvD`Uiia#z2%~2*%lO8wFE$grz7mT`Me0*Z9>U z7>FfcM_YuD&%8h?oO4Rcx_DI>c2i@z$mn__^^;Q~4^4fQ= zAMraUh;d-A3z0<(2-~1jqBsVwqPj8Z*p1Fb+<+HJ{ULKXj1(ESxZUzhl1U1CJ*(8r zjc|d}X5kPa_sGSU=ve$2z4~n}1~HBTh5U!s$5${7fg!9@GA{CFlZE#nbgoMb0)ZZA zWGNjQrcf0H&5uxCY9q9?4`W|KnqieE(~a)Tm?PsU=nijV39nfdqEpe;uJ4x$mXwuI zv@b&0(oZ-M&<1sLS#gs|Kfs}A^;#-TD8^>9UCHmm}4t_92E#k=}#lAR{ zvS`&id6UklQnpyPH0;)FqmBwkWon>djrl73!$I!wwT~ddq3&?LNHc_r=uspIegy8{|_4k8tnQimMhOS^*zp2^b5hwoZ)Bm zQg&lZmBda}QPD7HbP`uy12z27#I+^Ta__|tW;gwc;V`4ctG%$^OQ;g_p;ij*I*V;= zy~<}Ur{T~SDXKGT4L{tNzZdG<(YIa{CIn1pE!2Px&$d`W>BeKt)6C1v#7~6nEDxrJ z!{Cg`!Pn|P&-e+SvrJf)&Yz7en>P$W7%$~p%M~-#DfQe05`B98l9)#qTa$zM=4idH zr1{bpXPgcuTypF>Jq9`A(9{kdb7KI$(!^s%2+^TQJf$9e*6#rbkb;^&PV3~6?D zmBx?=dy475Lj*7@K$-}Oev>kRk4_VNj`bQ?W^>;Cz(H5}a;0n~I7g8P(^|(JF^c6z z5byR{Lb=Tp2#ZJ!Er_aJ;EW-f)k5!Jxt8D}{NtEMzrG@iZ*C}>=1XC5N%>ofk3$py z8P>}*bPbQOgERib#UQ@}LWkB!Qma8S;7MwP31U#;DvI%Ot6j-Bz#Y{eTYH7Dt6==N zzvQv5yOep?&3tM5nnLu55dGEmOfN&hD00#FB2v`}i#kgWof4qYmk|yASR6n z%5-*9$fMLIKPGw6o_^1)CBQ!<3H&f#acl{s)*~lX96n@tT1u(`EwXJ=yL}n4F@9yFcOxkL0A-8`mdA`jMTB9jyV;!Hw{L5 zY~HRTk`bbp;3#_LD~%-&;xN84L=cZ3>5bwdxWp5DQA~kz3NNsE7iD5clH{ds8?+_5}RJ z_H?U08MGM|9ul8iobIgDj3~VY^$QC0H}tFswNu5_ViopK0;Y9NQCYUsq;W8DDh$3) zf=@WAU1sH+eU#7l-`;vMstQW}RCfScF;d3AT>vIEPc(Xg2Ydhz>I04fS{xsR9=4r5 z4EEUVW*(q_pm6PJ+i?NA&x{)x)gid`04PL6m@S??8r+a_p1N_mSrey~a#Q6WC}Hdk zZg;t#4@!)n4%WZrtHm&d{8AW$!&*d5KBBZ05A+1|ixY&5t5riT^4@A|HnJ0A1k!{& zrCCrB<(fiwz-&$6DC|^&+j~k-X3Kg0M%$$0D|k>dOaE*YZQaLI!4DR~s1Y~rzq=*y zj(GU9Sv@HH-bpzlq-R|3WakguLj4>)J>6-y1&y0;{uLIjCUeg>gq^d3^$`NaaJ7fU z58K-|0plrR@QHD(nvRv<<_8&@#wlxKTf8_6YNd&3^BipYDkK*Etyo{sxXLO*+dzbh zQ^Dv3HJ8YIz+ndE)mtbP^5g-){sgUiUga$wdJ}3;VsIZI zE{e5@iY$C*WN){ZYar8aTVO=_vIS2Ki(PgvL3_!hylEZrsrFeHt@`h*IJtHH0L2~R z%?2+0MZn2L-|3lO1k0EI@LkOS`IN<1nRn`30eW-3s~o<70pz_UZ%^5#Eovzq~*BVCyqV3}fx;`U{hh~jaEOQ64k2)r^|cRK2G1-z0dXsDt9bPJ1-YiyXfWAZH}KSLwr3jG zw@<+;!n(sWVlC&Ogv5QI$|{HJF zBuURbyHnnApR74P5>GZm5QPj3sQXz; z=FluWBFkg^U|0z6CLq>t1Q1b0-w!?lOE-E|^~V)JRT|ytJ3|B!c3^d*`&nN?5NIo< zli%DC?7U(L!SUSc2qc89P^u7qBY~e+hJmKtks8}6`8n@Fr~VQ$8g51WY@9edCj6Zz z*q$ZP?o&@b^yA5hpu4woRnxy_nPA!l=YN|&o+}4WWUCN9T2y`AX^|*sXaFG?bbUym zr!b%b@Ay__tqv2w4f$!n$%UQ|m?Y$D+AIc0u_+vNP^uio-K z_`V9ooJ|9;&RB;+S^v9m4F~w#FYcGMB(Cc z#h@MHt3?m92!KS-D--JdBuZjxx%H5`{O=J8sc zorM0WY(UQ9-=u@ahQ(lACP|3w2)qq@vEVH_oTdD5`T}&)Y<5}?zh9_cx<_@>o=3GI z7iE!3=TQ-qJ3k&pa_rZGRWAyFik`>^WJqcIsb8~FxpVsdlJLOnPUhV>&a!fA2BWzl ztI)l8`wzj2Zg0m~rE-Ga{Jxx|J78wB63oC0Ex~;lL%I{n=B=uZiyA3o}i#l()EviQF z@KSgO<%B}z8!jLMxM()Q2=%J}(*neIwd)M~?X=eS!N~l-fxIa!8_LEjrzdIV*FoP; zfx)-WYbj~=cyY0QUnxSO_S&3&i0h}PbhVJ#zUBa(<+sh#`Wl>WuNTW-BT!;_BlMT$-tC9EN>D}!`8O)poXebC0yPN9R*d&x=!?U#q~BQ(l44<3VtdF~2Qdxd z%lKfWK;_kRkNq9~gh}d2!29Y57|(-Ozy53ZE}USWwttPmVM-1m5yP|BO%S6-p!q>i zdZDW`+AYkHRUT~t<-z%#?e=aoqu-(L2}s9C_0Yjz1$N8-$m?Y3C5Ue8ecHi_edncy zgC?A|JG$!SLiG`|Bju75;R6IJp$vp<^rD0^+eMD$jd!inAoL^{$|Nn)QgJtlV_hLN z1ShIW?s4{A4Y!cWGq#Yb(m~^R0^-t8od5`z!6Xk_KFV zZeC0AJ#ocb^l-I#!p9IIIH4aQFMrhyWU<{yrrNeHgGKg>6p?L@T~A0ii0q}sSCGVd zP^xm}GUJ-NDzI)G7k5Op=4CcluKS*y9uHkH?H>&;-mkxmR2x#n8Mv+JW*^g;jTaiv zP%l8-hGD;_q`riivOjZodjVm!hV4F|{PfVplD>iPAE)1@*bsH}+YL6P5(Pmv&4PPC zR#TaNNm8t2Am|&YAUKi7Z114@;^rgeVie&c1V11G#J7^c8E zEfJqtuVn4J{Rm8hD#V1>*(w9oz+S|c@J=xDo zig9Gte+7CC#}N7CBdUm3b+}fiJXbfjIluFrpT-Jf-FNrommp%{NCjV?80r}pA%1X+ z9Z~6kaRF0>iLEBjojhtxWbj*ExAWtERuohx5Jr9wb&0A9{D5`o)hwnOE0?AS|3Og0 z^b{M%eEF|J$lI}1H>JkhX|5WI+3q7Gwp#X6Mpdob&RFJJVl3bA1(J6YKeM!bjU&26 z^P7V@FGZD9e!-Y>Sc{b3gtA-<*<~S?O@JxgWNmSSsj}qtNFPEy?C$gX<1Z}h$vsoHWu_r@8tQHwn48ZH0Uu1?2#rs64VpO zyc0N?A1Tj6i^|dZql6o9>CeW1Mq%0mB#wfzDCrtKJOV=`^L`Xkfc)AKLbw#lV0quq zHa>)c4XYk<{pZ~eR9{Jc%YRg4qP+os>D@06p7SB?>M~WIxA!BlPG0hzRMUM!Tt}=f zoi*7xgFeNCw6B+2+^{W4_*m>1RDelyGO})!K_FSVgU;(UEFtF5%7@oQ%ZkN=7&CYG z7mPnBjIa>9jy=NSiWZ!BhP#mOJ}iv|8|74Obf?)PZz0dR^myC!5p&1=jZCT3LE9h? z$SfF1Ts zKCQ6BJnG!5aCjTw(y{@OX0}Pg)mY7{`x>Xdec%L5sG9o6DY?Ka9P~;TvEw~qWxAJ% zTg3z`&fH6_doO*d5~?TXy*Kxk44b>Hb3@19H4Hky?hO4xP{%J(Sc_ENHqg_Dq&Zem z!DG^>BB<)wUA3HOMWknvx)4?Nt}d1drK<4mQ>dTS0x2)ard$86*34u6g6$)SQwP6`1jy42F9oI58-kz#JHk^PM`l zbBOkX;E53gJ9Zc98Xo06rH%hu;EW?)k?pKSnG0gc)i4Z^6Ch}>n>xE;9d(bd(hEv@ z4`F}*?qFoxBp5Ax7936PLntxwC{g`$r47>`(`?7fd~v^BVnPAOtHxE3Pqji8QMFd$ zprHTs(lif?X$xcUB9nn~4C+JZJp>@(nq;UPrLV;3KnM{D0g=ah(yF5lj$wijqiZuy z6b6O{AgsF>5fLF;RFzwprOU5-3G8rAY#0oLe}&8KyL-bEBoY8McWZIYuX z4}wj8>n6CG?;019)l&6`FJI8F`J>lrq&WnxF}j=RF?4AqZ2Pb@L{DfUgAjp8&A4F^ z9D4GyQ*`?+bt((#43iBU$F_t_hY-?~z-+V@DJPE!Gvr~>i44c8;=R51#IZQlvgI8K zP~F$Nd^Qmu_hx#bh2&>)xlQUA7}jTaEttWZP%fBjN8{%(_5UL@Qjh4+|n-KxC?dTe|@-e zMJp=E2P32!JpUeqEX49GdEJg4;I_?j9 ze~OG&u!kqQ+-K+sAjU-Kj2PftjkD39>-4P^&bXZO;b-yg<}Y4BIBd<={JBLoK^{9Y zXB)n8EzNCPwa(0M)8@t7hHyM}&$R7&t^ zyNkL~FcK+C*JgS}oSO-^W#Q_pdPVKTZ@uWlEpH%%U>lNZ+PR61c9}IEe(xA^o4Dxr zcN0(dOh&9G12@bFC^;OZLi z(lax2cYFOH=D#w^-4^vS;OU|QqtZ*94rRm1QN}ZY%PQhcmW6%&+_R{BaV%o*7-VeI^ILVRgn%o+{og8 zr!Vbg(V_x5G8(iu3(W=G8fC+4{4Vn~SnXUZ-i_vMD|6vqW5lu17-#2H!*%T!GdJ>I zoT~>-j{hV-uKoM^c^$o*h>Xzeli|XCbG|4NsaO*%}p=V-s>u)L} z)=D~4H~S3PJ@3K6-Te=(#QaHzIGwF~cb8qRM??uez@u%b=C+tE=Dim$QyX`~~ zcsUYuVf@XPUQm9{x?gBP2)Q6Dfcuc@=Qzet^t;aae6atgtre5yQfhMlQ*)Qtk8-wf z#@Ra7h;%zx-p%m&{MA=Wit5oppS#s@-r0=<)Xo2*PygH?c|mJ4{AAO=>}r10m`Q6^ zbkyw?Q%(;@0P1F@=I1WrU<|=r?K*WQI~-*9>1@4lKpW?WW$L?%YP@|TIv&S^%y+c$ z-rsf8zW6+A*Dr)E#dkYu6U>?OV2h0hR;?1n_=#hM`!X}UuzQ4zVi+AFen&aLUQHla z2ebOyHa=^|15e9a-_*UFnaS!atN%CYk+-)Te~Fvi@o9$#Nh+!+uc&P5O~z@E%A-~a zer=VdRwIXz2whm^Dkw!9pafsuH^_@-wo>KgPSBmZv}Qf{ePh~R*0An)T~~Mzmw7lK z?Z?G?P;h35Xp?e^A*il-dM7#UV9dzH?xB$|jW>2NttKfKKjq)wGgfAVXb=FpMik4v z!M&O89G>X@r{8W7!8=HqGP#Pu_m*2B{WiG<(%MG|*f3JY&~L=JzYl0lA& zwx3=9mf8HE&d;`wDPq?DpMLdjr)i%5@m`MS0V_(a!uhU1FEK`^Sa_hs7~TagP42eR z)%OlRzP#b;2CoN2Cbh+S?ORSio`0fjb-bVTsrvYj)X7h8rT5)mMH3_E1y&wD^Njf* z5^%wGZpO5(KdF5OU{8bTS0*lC-3+Xg9@ZdH!hp1nMn}CBZZ~}zQ1aLI2kS)PiJsf1 z)9U+K-p^MHny747wyYy$I70_Q1cti=<^Ov9c)Sl0IQ`LEeX%T3y*N1coJb2O-10rw zMsvhpgylKLNvw{L}VDP zaevixQ8ISl<#;(aW6gPj-I?djo7FL)bZa}TPy4 zb)HM!yEO1J$y(~D&iVCK$vo{0xhL(?W71bqwo|$P6l#zmCV=!>1uHJSlCS|!%Dn?h z()Ws0!gKUF&_74dXu%xzK1P=54Pbo+JTNz8R7SifU-RiFMi)T z1SAUwko0ARdR603yxxjJqyY)|@(sZ5(l^1R$lV@k04;A3*+8T*e36NiJWtMNTd2gK zY&0t7hrpqFy2V01(uTMw_8D z=?C^AghhHLZ_OQZuC;m0aat&c{P>VDAP+ZzxaSojpC z{||^K552qw7N(nhp6ZuDLSs4PaX`La4)+F1+6|^lz^M$fq#efjeF5`c0gEtUrw9~$ zA8fS`xQcs_5g-iy$dyC1Yj#2_RR+i>P}fSI zP|m!Y{9K!L{CCw+`H{~m0KsJ)%U)yv_>Z{*7Pp~20x(3+ZgXk0$;muE(G_0~&AmmZS6z`kpdza<;^-0sVH#mFsR0QpOiqSDa< z;W3y$V=VA4Tx)34c*S^@Dn{y;ay?N?^rIs%X@pn_(-5J=&|q}~<%}jujU^s);@LTn zzhx2(gNIZ|xC5FV1z|hZaf$^UU*4-z%Wi!fV<>1~LN)Nhwi}kNa5(uY%a5nfJR2e7ONZkDC@@WESpd0#l{0-EMeq$sv^X?FOC+7P zTXogUd)%(A;SlUB;J=6>PNlC{g$eNP;a5^`78Kp<*lr^DouF$$9o+y|mmIT;+q|q> z1ptrS;p&&rl2-FXo8?o~A9{|A)zxy#q)TAS-+akm45C9)Z_A6--9Y$;cqDOx@T z4T6b4?T4zL9zUWH3;_F2+~b!POlnGL2l{eYZbK+w2iOhrOzT}v?0SL$c?KSpNrtpX z%!b~3FXd;Dhzr>;l{WbW$+Nq(D-Pa#PF#c7t$> z7aq3#e1{eww9K{mP9>#dqK52~)!E&`Z$C!}09a)HRsH2i?oh7gPlwH^+LFgG>^d;3 zbc#P2Oc5iOB;oKx>&)Lihu%fou6aOM(Gdg5?(yGCjo+*MWL+6U*VAZeX7%qZZW?q=UI5mc}nk_V9<|y_%_?+w0pef z)^siCwFVn^SVKioIo_3YcR3ImRtt{}8ITd|BSbyXZ@yguS9TpW4@J}n*uf0 zmurNwVamQu%Wv+{Auw}x@jiOQyw?o60QAi)Hlh4jh&hujh9_#ZF30$Zj=>`qZ?oNK zNGEE^?xT@_Eqr(*4#l*@NkvU{g%UFU2INW4E3y4lCEEMd02M6xqO+C%7Qf!+tM1oo z^D9c@{3mafj~`hrc15kfP-pziNd55{;m$FYjFr=Z1;GHvq%WgD0%D3|2dGyY=ATiI z9PpmJXLdb5F33cWS2EtzFek8jC0Tg?l?3QDHMunXQ6fQiDJ6^`5s6GL6L@ojhAEsB zySTDRemlAy^oC;gI*VBwqF@*;8q}$7f!0^D5F#g9`tAcQkuxRB4#@e?&eY#fhA<_1 zZa0<&R*0Cvs1{z>oL1gq{PmY3jv2o}$X#!j;XOzQI7DfknB2a&nNgW(Ps7e7FXK!?HM@=nyXuUs!97dfJk(Wf$*9o^S` zPf22~o=}RYB)7mLIE@Kwn^f z=gTk%A-U=p3kIoiaA4R7IZvd32@Bxcsa7%jUK8dYcBv`PQ0HeE{lVYz9>!l^a=N!F zXE07w%KdeT+;YKdg8msUueTQRCMMoW8g55Ki@KM6JnJ)G7KR9^X%Ke(TlHq;yi4OJ z0qN4U1OY?KS8VBi6`pZS)!$~GJdZg2mwD(qtkojeX9u+ZeS*%^hS*Gkhjpbr_a42~ z)8$1*cO3F%`C}Nl%)TxUbTiUk-c{~^-EB5+^KUF}6w+>Bn$B(-RelMxaSNs+yqSGL z=cu+x@21zVjN&&2OLIub2aHwvJ~EYie@B!zBy_@GPVDu(@l}bApmDi1u4qYJcwI{H z`z}dBn9^VmwVuoguNRiI6fa#?^oAzB_PMG3N9p8d)guLJ$+Y|Y32ZAdO2eJ6Dj`Y{ zHt<9T!TJrn4kJ&9h5@zfuE(AI0vh{G%8k75r0=K-8NFeYQB!U6hWmHor#OuWC`V;y z>!$^cGZHbLD|s#rKZ(7jY2&)H2X1)&`(al?^|HI^@36erm&YA#pe5yr>0j+q{X4NA zh51)3rPlq*N5;-*3xqBmgDHiruZ@MH0zqJT4hgio{^>k~(7#oN2m7T-4PGa*RB7$3 zU5Vv@L(!H`Iz24*sgRj#Z|5A37eqvq zD&8$eh#T~{@TDCp;i3qz4nkU0#FT3=t?0t%{(et4j>jaE9Zx8kcIpo?UUs(4g(@L> zytsG)>~+!c#Oa8&yiroIjY;i$pVYeE{gKE9@2qQ=DFKE^D8rk&kJFx;Sqk+sab#D3!RS; z==kg~ilcIP-CY=(7clw>rPMdTP?-W0?Mk; z`l{AXLTDPiz+&DZw#g5~ouKK*ESituj4wOWgoCwQcNFTpj$ zt$NVPH;VE1K}(nP*@02)>D$9@QiOj7WBged3HPAY$Bz+P)|u%Hw{`9i!rD>{n(syW zH`)AcpPMRLsWTqdc>K@~o@ng%;^2vn=S~DiUZ=@KQpK_mRsyRj;`gI*9cTdJIecbx z+RxHUx+bbgC|R-}{)s&>uQXd3m`gLJP&obmRD7|FN{q{-!hp4Lr&BXE;1N^SV|b#P z_39t}|Gnr@Wo70mW=b0>@HT zi3#aNYis1W(BGspS_F2HV)b6KNaApYd~zRz7{ep}(8eV;7r%7kLxOKuC9!nzddTPs zEk1$_kZbelB}6wX8wj3&5EIkpuyvWCLRB;ya3J@ltTI@f!vOmnR2H_NIuN4OVns1( z--&$(E>-M0^;ksnodlMAY?q`vgn_;*!)Hzas5jmKS&7-dS)YfEK$EiaU?svA-R@u2nESh;=ntA| zhUM;A_-#qzy$s7>coTNO`4i&a&1l$HQqhK}c|7z6xQqHgrpa4oQad^yKXF{S4)Puz z{{%X}J+q6Y)}+oSAvZ%AlAfQeeOSR;d+~FKs2PIAmlQUFZ4zn^`=@0WQW}ylzuZub z(NM|c)@Mh}0%!3a@b&hCPW^ulPi;B8ccwCW9&|lzfx~PxPlNtH$p#P}Cxax|;$(k~ z`|s*VGU(=`Kv|X#)*qkXMesm2e@1d80U?db$>EMD&|HivKLo~>Jz%aXGgld72tUIAh*T=vI0{ zxwtJnvHL~BlA+pAE7*}s#BWzCq_K#eT9O+;)^hLU-&RR9U6e|mMeFm?Tr~<%0IM$p ziHT0c8gMaTf&Fw%62cJNb6G#_BCns~(z1J3KM{pml5`kH+X`}`X59yb)u4a)&A}c? z2E{}1b#EYH!E1hO_kVv?yT1<{%*>%o00T@rex}6mV}$5XK)dp>u3qm=h#um5^hIsp z;aov0Tc4ryE3{ghRd+O|$#d-s0}!j-(vMGO?Z1jT4jX#EICV!&?s)m_Hy)oaDkO}O zp@J1H7@%MIa2Z=1-nkD9K1|(+9QGUwD5g*g`3(p>L|(H=i##WWhY#EY!vs~IJ2-(h z4uCYNmEE-iiua%}sf+^!jwz9k*%&Zb7wHv&A-!x~k2=55VYkjV!)G{pa>am+y=1e1 z7=H#}Xs3OkPPk6+`N@NT$UZYrA)$R-FRqd&Zid8go8-84JtR&W0`H+Q}jUi*0HYD5)q3qJ(I%c2X@1N+q` zPVpbwa1O?7hTaDXy@hE_jXBWQAUvFj6&6I555Gk`Ne83SFj3#%&TspnpxdNQA#lD% zViG)bdh1PMecZR3kr;WRwx_n<;Txg|D4!7O4LfGnkJ$9L zK#7=`{4mhi+!dd=;$;@k$yRUEvTU3A6>5Msrr}o!mhj*l)$-P7(c|&zoC1J)G~8xg z)o%3cVYNd6leAVm`6HhdnqkMt^K`x8?Z(PC;}!b6oHc7-|4$20v`UMN>JP3Oo$sW<0 zh5(g+^LA6oc4?YYKohYK;RC!o@@!T4@@t0otsCA$v}~}+8PBo%SLwTBh2CMxNWMpMHTHG!{v3_rJ?`AwFsNSZfTtVw?&s%7=0>75ArK96{EvP{PW_bg-Yg7ROfLq%im=)PBwQ=4=MF6wU z$|P6VbfLPr0EAryyD?oPNjGSBUQVS&iBj0?8S@C}iXSH3sL*lS=_V|a_4sA)B&ex$ zjipeq@cg1n#N$GKE$?k~cBEJka5z5~U6S9e@LC_&=vg*3(=CKYoP*zANT!RE7hqF( z%{GY!wh>9$W$Yby3QX12$A}fOy`wOjzt9$lAQ7hLohoUmX9<(sHr`yvch}<;l<-P> zg?Y09x~c7CU2vAQyNVpY98wgQO;;peUCs<6A+Kjck~*J;R0iu62r)9C#B{2T7AU2a zmn)zB{@qS@028Q9+NMGsAP2Qz9RRDfucc7EwZ)omf4esD2Cue(!j%)DVoU9tH zb2c@XsR`y4NoxP_P8^28tN$wbtzsXo-pHiBh@tnOxbyG^`wP`TBg=TclMR47%G58>Pe~a>VpAN!wsO{g z%P(QgAU`i#5r3Q##H|(i5$HUR0g?0^=Ixb{B+A?__W4vl2=mRq&XW-Y1!A;b+4*3M-1Ix#4D#zu12i3{2S=%`a%2L z>-wKsGvfF0p#yvK|Jah%`{dYI; zvae3)A3VLBwar+sq6VnOGRc)1NgPLZa7Acg%W`+*G5@eASiu}2pndxp`dTxXB$MeE zoax^(3~3V*3V~G8d=EW@K|^ThvPq9R3I-JmJzKJ5i|(gMk;{|9kThRHT!Wock|APr zUwU_d;3xJXqmtRX_HB^m|M2ynQB8eO+c%m7LhoG=i1aQ+nzYa~6e*z#Dpf#`Djg&N z=_;XvbPQ5OK#-uI^xi~}A|O%~lp-Qkcvt@S{k+c^7$>)ZIFFH|_r-SX zirucxMGu#rU!*O2M(&osOwYkE_3F+m_0Xb3?%2!{2Pkr*=%$O`8-vTAEUC~bj>}}? z7G!s_vXy)a$g81xd`%xOQ)j;7f&Uxb(iSz0mX(s*A(n`hg1_B3QoC&}g&qkiLMf56_Pz++3;$V;qDfo==lu)#_hh!8 z(_xhnOoHS2ahd+Wv-?DBW9kz_@t@##3>XnIeMLxSL(2n%rKR8#dQo@vpJx-2NJ!|L zp!L#%!Ifx4t$B!;%;|aTba<{7@DRgaDr0 ze#ElOc^lqc$vA*{FLdd2CWg*7xL5K?&!k;P#%UIu$}raYIAx4}c%CW!OsSylx}Drpvn_nYQlNHJJ$Vz`(MN?II0UA{(>ewNFA?~Uty2}uL*heoW& z(icZ;E}|URk&yk)=42B;ljqsN7hbi3)Aa#3oL{O;(9Lotxz3qayfDP1&xU`NxYhUR zsR`DahJf_aKtZ=8dfZ=ih4^0fXTHA(+?5uGWxxD*H|XxH?+9z49K?U>yz7{MdZYC# zW>t|zc1>eT4eZECZBPH+;JJA5BRy7fHed0ht9ALJjjUl*{LHNgdY;@pSHc z(=^5Z5oF?v8yFMdBu7 z14YkmsvCEU<=kp27o|D8eYpGfFGqt}NDceIMbz$XL;~qz?d*D*Dw5sn;Q?17BXa`~ zAC>)SrO1TDEC)sCNw4p1t0s@4Q)-znkxb*2rV12a(cuA`qO3&8M2O{gN7plvR|KSK zuZHNJF%6yj%qj_fZr3$#$!A0d5yKSy_K{>O4@}5!Hdp0@K7;wobG&`z!`{D!f6FBy z%09UpmS=_-7JWvpWXK)1NhD)2&n}XxloHi8Pe#i8Vdd#oaUlb*&X-c!ItCwoE(V>* zZ~uv96fMcDc{e_2vzDH}T_G2>C$r-ECVQJ>In*u$!$vKh%8Tz~Z(D_(=X)f#*fVeW zC#HvKII%B2?m!jVlTdG5N#t;m$LV|M*X9ysd~-=~B4r39a$WWiW04y)KIKRVgqegy zUEI~~%1G%*(G#vH&C~?k=B+kKXeG@XLz?nFfD6!6%UJJ$i7sp|(pk_1QQl+UqL2DM zZ=(ucdW-l=vYME{&Y|t%w0K?9KQXOJPKava%>yJ=6V8m_8@7nuH1O7h^?2D7RCK4v05Sm5mta?Q$dR>OG-^SzJ z`_I9N+fNotTGG~VDAqL0@_$k+QCtn}Y*0Lgd3o1#E0hb1cYjWbHqh5t5mtf*}~5P^a<9Hm;QsvPg+i&U?WOBXz5<`Obl z=zHz|3yLI7eQwS8w?hkaRr_P<-08Je(=3TfYl$&lZj-mWOBH1WXn5`}eC*oCCrz)F zU1iYS@=_=BKy!%Cgh!EznzISPSV+{jZiLepVRA>YEHVs+@jq+*-u;3|J5pjVpXJe^ zzQOcXoBN|!;*MaEq$4FSQkJkRVvJH;khn=%V3T82h{XcLL3 zV>!m2jkG1}oIr*HvDrqmNDSku;GNS888jxOsiNZdgzkjs`_wp`2?b=$<<5p=da`SdN=tX_Itu=|y z|44>qUsRY(7sV~LP6+?{x_SG-yT23)>tFwUcz4S5X(j68KbJ+Gg8a%asbLVg1G_mt zRce%;Lds$?^sc5aqAefe=2)*}l{o+8=as+Y zxxFM6Iw7lyQFFc|H>!7YFD}i6T{`-E+qZp9@W-qON1f1$b777iE^*)JGOC-T)OnAy zXY*7B-Q4#aYc5sowS9$pr5GLst9W1-^N3zGV1TO?P*&Y7)wGrhOr zlt7X--d59$X8FaDk(ym>zVQyfI`H|Qgq;tR$=+zrObV3s|L<4mAVr(XT!yZb9{S}$ zcdwS~chx=GRZ4UI&H1@gnfe00WSqSu&lqIjrV3W)B^daKTo%COx!uNGXG}`s@2B!E zz9T!e-^2O?kW*ND*fKOifa|XCZ9{4{-0vRHLi3(!T)CXQ|Eu-Y$&-m_qM_>*h2*`_Jz#7an2jbb3r6rC zT)G~I?BXJu4>lCiSDVydkt6C~Cz7ze&H;bZ{Z~g=7S{aRWOK%}1Orj-gvoda@Ny2e zHvNrt_#O26&3PW?{r+bgi&Btyn|aKa_5*$1`yIH3?19FbJ_fsuTdL-#8(m4G%A^{F z)y;)@gwmrAZUYsderrJvC)T2nJ8WiKYrwZEu?@=fe~m0BzO{0rFXV2=3i$Ma z5dXeS8gNPi_HTY0g3Y`JCXm0L+!=~7-NYXwgN=DOoD-${dO>39E_RHVP<~*Mx<~Wh zs~LMQa8nn60KK`<*pUOTC(718XReBQQza2KPcccMXGd3XiFaFVYKCFO#83WSDZp}p zDw_Bz%Z*Fkat?@$CJPU^9JTWl5Q1kKgUlD$$cW;F`Avf>d+rF<;n3H1xo|DwCy!a^ ziO912_o~PvMY41(Q;XxWF_U1PjrEq$~eOmifO=c6o1O|LOVDtNH+m^s0debnZV4 zg5+S;-orNXJ$YV zU1mEX%5G<`iHiNBuS7ll)pNTw>8!2RRB*(AvbZ{!l{H>!;O)=>O0<4(NFu<7Gx*`5 z!ROB!r#sQkOUyFF;u|mrRfveZ8SvP;0?ORa=jT8J{ZEYrlCg;Y+R#T&c@S{ql)s0M zTst6=UM#DvFM(y63UWaGQ1Xi==@A%LAT|F6ZifL71FRbDW{tZ6QbXcj!}p%3I|1&7 zY0N-{0a3374v)eAjq_OA|DN3jaWgjSB9OW}u4(pPr#kSQBbWm4B0sSS{yr{H`z+WP z*mfE(o?dqx3IV$KD(LRAn2!T2i#lKI|IfGf?rJ7e#O9tBP z1;|ad;&c$JH95-uckAzu*T{hoke@TQ2)O}Nz5_o-UJ5&N~n+53nY#>?*yOFBq@ z<+VKKRi^0LAEpA}qd^=UH{NrmJ_UfjR*jh;!UC@)WYFNzzXAgzCM2ajcFB%{ zcfHTWt~GuBc;@47g6P)i-bg7jx6B6Ss8pgz4;Z4aqP zgn>~0XR$~Zw=z0cC)UQ#h;iU;5)qR;pT^eH--CVttS5>y?Ey=+VnF*Qput5^CuV$Y zBtIRyj(ze!PK!`oqSNB<7ogq!_$qwk8Q!P!M2|GgpT1Uq=jvZUjp@L}HtcL%IXTl# zBDy<(*y_j&UP{pQy(w}N7_3-zE`nw-M4D-z#QLMK9=MP`)jRToTww6h42I_jf`1#d zUE`zoOv$(8Ohr0FUiTi5D}BFMS|by=DNs-Pa>0J|LCn?swiI&}Y^59QOe$G3wLz|9 zu;kHY{lw^Zv<|y<$VrZpj;|<{5`Tc5~y7ti=p1W?>d;sDE zR(jE>81V7oiSS&9dL)}Jh@e{a~OgLaa?Uq1^`m?_kY#t98r+tT8 zLiQ&1DJb|_Al@s!z)j-^q~d=buDpmYZ)6(D#kuOCWxM2!9<9M{`(jTA-t(oTt4<3C z$g38zODM?#-*o%78H64 zCpl!Ivsanrs95jQDRS1la()DjEC7BH47ZoKP}ou)hA%xY7!fo6h&t)7Q5dO$-O%XH zx6a8@G8B~g=YanzEb6;P3qK>um)Fb4G2t}feH<*tU`%kSL_b5U)IKJco}GK^nq45# zzy}knyh1rv!u|2O2*1~R-~-$A7BLV(|KVa0y^Mk;PalJ>)wxx{X(G$M!Pk|U7J8oV zM6&GAt|yBqThOc4zt6BNWY0+;rZj$1^T0sj_vwaW#W(jW3p!4TL0d6tdhRSggsy5z zWWk>#_Kafgi#}tc5Jy4HXu<`t6`t61YO(GoS{m?B>(<*Kn7>gE!ROh=LNoi}2*;AP z)^`1jcKR^~T}lz?%`Sfk5-CU}zXPLS%+@ay_zT1FtwjZW8~G2cv*QKaHBxQXALR~Z z3m{t6BOzqbI{-;qbY#EOA>X0(gc674&3MvMqV+m0CzAgw=7JC7_PpsPTxLEjf@}~p zu-Nj2%~R{-3eqi&yw@o!)n)?F8y zRZLUgQk~`Wy#`2-6Ce}RzD}5lrzGLrc~sQHa84qy)B*G45dL3(N+eK2#9;Q!#{(er zUOno(tKE_r&07$+6x~50EpzbkI+T@70?A9o6~b3cwW+awpY zOCp^&V`EX+pm9lfZ=lLDwfr9b_$bJ+lY>4)>WyFtiN% zW1_4BMf#;U>lCnZLynC&nV6wf5Hjbh5b>XY9a=SndU&DD9e+iPlaX@Vb#YB`{gY9t z_Ls(XTRaUs?u9N<&bTROej?PC5SJggU3G{iCbM0K2w=ZRezL+*#$ZBp7lC zIA&OL>~DX)b58f54#W2*3K*&p$+l4kN;tmh3`!{OEim2%><_K9Arn(|J;|fR|0PpK zTSzR&vg1u&a9k#qrrRA0r};s07f$xTj;z#<2fuB5)AZ1XOPOKbzyxlQ)2Ykcm7@i& zt3~qiqJ@6Bv@5@2cCSu$3WCdOf@$%Q2h9o05TZT?v4)`kT$+{GF{&l_hd>{d%ZuC? zG9F4GHT@JA!nVOMT4L8_P%L1&1|nXLaompxn-r+^&@~ym2+}5!;M?|V`G5G2N%wgF zWx%PanK5xOw1G=Jki;XL#-~w?@YC`Pwj>%c6DO%ABjd97WOQbJ+7$+(%Ht)=I@007u zK$!o4IPjGFI@T^l(D#xw2VgtA483g9?ayK7ZgQA+;ZMHm5!m*>7t#Gn#a*fB0w`BA zF6B`t+})V=jjS*mHSIwH--QRh2KE|dCcc+tMZ}*L3H%_ui7{}6DUkCE09sT?N0?$X#XZ*J5s zR~l_1Yn3aR$C)9o;O<2`D9zul;T*}4i{Pp$=QK`R>A^z zBTtvW$-!eq=S}f5N>w4rV88cLLODv&9Phw|34zr(HN}_GNJLe552WZ103()>r@@!~ zj=V38ah#h^nd9MM5`); zhF=`=97h6uEzZL2$?c^wWjVK-keXC6yC2HiY~N!?QTt}WB(=z zy=sxHdbB`l0f{F2quxat&lKxsEfAREDEFn7rcjBBDZ+{ggx2Vh;URc{oGlT_rNi6h zWqxh!-S09RD|TN*vg%)`@-+C?fXvoRoJ0(;Bv5v!=m|ItBQyuNJhz6a|Iq|NBHA>h zuLjuw!`I|hW+w4KA@wluKF9FeGvT^=}L$uRTD&tv1!`x$my}bHV_jn z{9A&A%`Q8Iz@7|&s-=zy*T$%8W zt)tZFU8HDE_^kY{B!dX5`|9IQg&9DSMXyACt2a%6u=*jQ6;iKOGMPlJ08CUEmbK2H z{04~jB2i@zYOF-yR&bJkMx0n@&zST;Q9SzJbn_tA68Ey{45&<|-x zyglBiYJ5)6ZjDd*P0n0$g|X~W^`K7r4cyW#(w1*pU3bDj)#d(BGUgMEm?=|!54e^6 zlObrqCusw4l!M}&-%q!I0f{8W^jT{kG}9E%Kk5VJIxE5o#e?e(d|vSc?XUmG3qTF$ zTD^#K)at&~*TVqrw)CXgukE$49Wi~=P-HiHv;`Oqw&`3AE~{J31a4=L;uFPb_c!L( z?#%6}knMlJ<=)Ad1X74N zcMs}EAM%@vMQUX@#?h|(N(ZOn4rlV)Dp`LNXr_I-n-IxY7bWP}%Q1X}jF}2e)wu3v?JKDq|w5`GbZJPHd!=of@CV zneoK?SDh?Oi$Y@Aj)O%A5%2GUo5Lc=ZoqVWck9Mogh}g+FOu~=C9}*Te4D=2dBK>p z()S_xIbBhTPsYwql`VocHtAMGmlYm{CS0cY-1%Z_h_&p6jC9(aKnWYFLU;!aItNoO zjWIvGH}A&+V(~2gn6%P1C#ITxhqgR zCyJ`pn)w(d{BFxW7SCD0$NalNhB#*qeayM+|0rAV^Qjf!IpcOim z>FoFx2G$3j=Bm%@J(3UOf+=kZ{2iAfUgmzCGbO7lFrC{=s`7*_DK8J?UJu;c<@M4r z_*jRu1{pIpAPl7oQZFn2_mQmEatAC#U&GKWQapyrxZhgl3U;B*vcW=M2iY9esoN+A z2>kOGEONr*)Wns5S_XHu4f8vc#N8l@7>8f9)BUqS%81-7{8R6VJ6m-5N-p!eP#@*` z(zl?<>Hq3x725&Bm@mI&{jVQ;4J4C3u??TEy9^TDRp<9bvjm64P^KnPO-K4~RLax1 zLj_36Cvu^_=)96a#?#CaSCEB!-3l0t;ulbG_xaQCz&CSY03kK=umfFL9i1+&gy@b8 zzYCAACu$@*M7-3;nh$m>>b6_0wbABMp5EI-d2m0#%NLJQBg%3V(LgWjFrof9sI4#; z=0IK%oSRi*l^c`eM5Jm3i8tCHmWjqof*$n}cE$&sbygj-0h>#-c)z=$VRbf$55zY9 zo&B7bM{WHA7CL_YFExB81Tfu zaz1O2t?!IreKA|xk`3o0iim(>Mp3^t7gie9fP1-S^y%XL$}c9C3+k2{oEU5u)QTo$tG(hCIfWO{RxsA|20mWATolnC0GgY%JY7g7f_V&%J zL7%fVUFz1=ms6amQkOT66dnG5XmH}}w;rUxegk7wIfIT`*)t$Vsr)zU0;ogi#(Nev zpfHyJ+$^992<0kz69<`y)WU)45LI8THYH(V&$bgiLChxtt;_&x)1GL{M9m=F=(~JP zH0*~={r>5X<8t}`ecl&kFV)Y`KLP_6M02aBx%8>f;jURSB}9yP4VHvlA5f!Mo|)Z1 zP7}2^5G(p@TNt*bJOli5rJ&JVp)U4vlNLWNn>$Zjo`;f)$i#jehbWnzoh+Ek(toq9 z`QXi4d~^EQACgu{=B-wuvHg~+$juS8Y&x8v?Njrb6JT`|U@nZ;%mBEmAZOr%q~a&M zK0F8K`h~skB9^@JUD5<_PP$K&r<~<{{R*I6MD~_BfNy^zap@EM|5EM9QIjuit==E4 zaYgiiIS9|GGG1?=9__({zWiU!PAm~1B^vU=fA?#twE#hc)653YpN6@%F94vl`W|3> zWPnT@^|F5T?;BtW%d2leTf#(MEV{-{x#D)~TcV@{ypV!K99F%{AXTOCs^C{vaZB@e zJ9qRzdqJ<-^?;b7&}G{Jp$Mty3nbNouaRPG_9fzq2i!CNYN(UEav${OR(g4Xkc4~l z0ie_TD&eRKCl&Qn2;Q-WUs#G(2RhmjaJjAmPwC`CCBS!51}K|SV7sXg-gRu}VYlXzk&R#%%Gjot`8{rZpVAXr%IRV9N* z)Sok+?a_ts@IN1~6Y*s1FF(IbeC-WYth=oq`FP^?2Yoqd{D`k?+3(v|ec9QCS2r$9 zJ|biT1=v#9U&wST;Df6cD7eZBAE3!(F)0twLM)=5WF}*QQ1au8iAGa3x6bPd$psEi zvSplD?$KTSj%ZDOuU)9CdyV_*&)QCwK8#r40*`MO<8dBP-zcCgo$5>L_7KSb~y5WE{7 zFSr(?c6l%_0oyzDSE`_axr4Z#G+-cJmHfIT10-6a(UGQRcu@X{?7%j`di(FpPd!#q2B%x>h=3*h6WzR~E_A1d`6MkO; zl&?_gx+^qWgo_fZBd~_7TUoE^quM~H9P!99k9a#uu`7#{>~_o^q~gG4S9Z4~anu5Nw+0A$fuK=LTD%Q5y)6nPH} zM0hJs9}q`MT6|^iDJXa&fYDL(W>N4j%+*F9fAH;#T)^p@zX#@cnI+(dO#xD+Es;$X z(P+B@4gDbmOr?^p2gEppjO1dB=w&2p*|lBiIg0AogekD}Vr#DCG+6(TJCt!-l4>Ew z9624;f~CYg-Y79|w^cGvbzN}HD^3#*X&ct8CMOZ~?zwudPObP>FQE3>2q%E9h_Tfh&t)NRkO-RGBt|qN{t`9n$leUi+rIhI zqWQoaXT2izkGC2vxP{0TzL2oN&3dW*E^aPe-J?hUS=wKtSBkXKW%RxeXcuf7&$29+FKF!@TWO@E;Rq#*>TaKvo$F65FmvwH?I% z(dygHnon)kOM*UD4~Xq>Rbnvm2Y~f;F#B?Yh;9a$(IGZnd)4*#;?c@7*aALhC@*$? zkPCraM=~#V2W|rS6>ZgAJw9_mClu(is~{1UL3}C~t^p4hKMGCFh!aTiQQ3K56amJ6 zTb?%{?Ptr9f2-U6NaIR(T9`-cw|WEe`0-|8OFDNs!!1IcUCEOd{yR>f1XhI@{T$GS zl4UDhPf&>6WW;Gjz}&u*kS4uzA}99c+9%`2b>^5V%|L*>!Bf>^AYJrW0`oYA&KK3N z4B9|})5L)Ee5DL(h0KqHw-CnbTX4_iDh){fiwNJ-E?RB`Q^^Xa7Z13b+iMKs?bYJIycVBi_g zMvpz;@-dL_@7!YQy1KjFrBiE>+x6Neg82j+7ArXm^0Cj)dPamXiQG2?>>|Og_zC1t zw_7=J8|K17yq247yol}r4qZ&w0QWvsu%oP|Cdp5XtdPV+CW4-VrdsRup}o*@er7n| z@EtM3a|E1Nb8h)!)EIujMGqNdS$rDf3z$V)~rGW?2jNm#U@ za9VWNDPv(AEVr`6+-~95K_n8Wxm~G0moM6MT`VBxFxzk&uo`SqkTdP!=uGaL%#hJ<-O{0ej6hmQ&79o2yZ|b$?k{~c(%|+~?+ngh3!RUHB3+f++yf-* zyLAc$V>q6`(;bho5xO(db*Y9jhC+m&ArE36WjpUa47@^Jfwgk?zzDZcqRXT}E`{mf zQCMfTxlrK%d7{FCk06%!n4B=uPhM zfzJoWhf1@!)3O3XI{h;WC-tQ#PCmqT)M7|P?H@g-#;~7}VDeO?KIfpV%Lx8>6Krto zm)+HL5+N;QJag(TeY_Zscd;*GYS30rgtbe(`)%QgQu&qJNNe|)I`37uS8(#~VCJjq%b?cnu#tyeUtJ6R)z=SB)IJDXF`_2F4i^`cA{$v5 zP!_hkhX?Gg2e*|cm{A$p-N*NzFCY^I?qFEh#L=HlyfFf#{7RqLvF8645F59s%*~)! zbNNZ5Yj=EuwlgYAkd@}}%?}SJkyJh%lh=pR<3!Hx3LEWAcsjm7%@kPT_?|}7)DWBS zzoaX5Dz5Q9&gF;2U%#|txf%w)D`mZXSMo5NS#M{WUD(SMge6F*e_b{k>MQ!?N8r_OO ze;suJScAK?CDLqk-f#-vs?6EzHMZ(Xuj~*pf{9(R7mW=3w5c+${DuQ%Vsu2GIS7+!sE?$Z>esuh$u1itALu&^Zn%J;XRaI@P)Vk?c(AJYi^{T{isC zM-fc_bmFU|h0Dt}3; zF1cng3Twi27nArom8#iH_F}@S0V;HB+~9I!Bfo({GPLhcH|I`JspELmCIZKVvRHrF z`Zu%!oE@at(ZRjeXcpekZ&4W?S^Bp4+6NG7BrFqWw_139TDIIriKGnX6S-)G;N6-K zjL*FlpZ78VbbxYLDJX1#JmR9-lNWXQ?DLZ6mFdLZlwNg8w;MNvn>UPHGjOWsG2 zSQPP3fK&ftCTKkPJ~s5N!gP=F06N4_*yT&!#sHZHJQx)_*25CWeQT-4Yv`L{Jl8-gfKMIn_|fHL@0H{deORo}bhILZ$6rkLw}?ag~46EUSJ^RfZWO zGEP+#_7RQ1;iFOy-6X5k$^ix$eui0g^{%7EH42DhXzjB@nDvh4d*^;8|6E^bfjU$h zeGG>_!c z9{fn%-=c{o+E-S6D(r9UML_u4_<9jzcW)tdsi=!YvfskWb-n;Tf)4==eFcW6W?N8A z7aPxor2|~f?6(?vwLiFj^nd)g5gRXE9j0!pBjA4=O!wfpPR@jYLEA;K{c$!FC0;DR zyR>-z=7ViYd}`$SXBE_BlSlI6UAwE+{IvxpwJeN%k6kDSsVy(5w<+6F8+-zeB02G7 z7Yb?`-j2M+m@rBT>Yi&30VLxsRWwa(pE>V<@woT4DnxL61&^=5H1liw>?4+fYR4`w zLJ86@JOydc7vEALNiR{1wQ=`55q|h#&4Hd(t-hWJRenFw=rMXH?mc10Cx}T{w^n(l z|7}rza`94V5i5>ZhYfLBf}u0?_8Up$_H8xe6`VKe!&Nrlx=j_x6}ggUYVeWOot~HS zFg_nz>pyR+)cv1T>jExl4(F{>uoWI@l5;!DC|gHH!3UXfo#bkmrR8i8=q+rfacdv% z`c_7K67DAPji_C`*%1KRCNpn=!Q$uGcsP>kfHRZB?tXhmamAVa#gdTkV*a^MY z9>qm3?$8YGUL->6Bl)%;AFwy0c@K~Br(UKQ_^XRz&3|i{iz!F{_rr*V^qQCbs*Ez9 zNMFL8LCP;#CQO5n?lBXoC~PpHm8ks91G}Q^6hz4S)wJMsKGUdYb%>O^n0mL5eAKBA zciPD%3&`0yLh}3eRtAAT#;u6WC57*AVlaO-Y9w+V3|wv&+fC;EB;{f#T^l?q2ziO}u5{nrX!Use z+8^yRk!o4O_Vlk970Rhm5Qlb$4tG&9_6?0l!CLF=puDeIekcBN`{pD0M0&5eFalHE;TT(HmI&^dTG*TBeGKOumb5x;2}2N!deI3M z$qqIwb$w<*10>vnK!DP9si6ZQA9)sTu!nJZQU~Q5N@`y8dp@SY#?^z$ZRnj=Lltv{ zbey*MP|5v!wXao^Wa=JwSs0w!N%8R}*Tt$ig6QlC<=hv>m#X!dcF6c5L`w;nJ2S&zmSk!w@i-z%YwS5co z7`)5gz5Q*B2OVUOQpooc)Jc^L$FQtzi=?h;EdN7}gd$@z5)>$x)^09UmQYn(z>p#y zdwT<|I{-M*0(yS2O_IAaLUS#l3cZJ z@`L9(?UMY3T(OJ$^`)ulVO^+cR^76cAXt0yJ#dq6eEXSlj_uPoDAFB|h!e)%fmx%& ziMpI*OBJ;YK<`x~LF17KT)u}y|DE2aY-4mo7OxqCjr774_KMbE755gf{XeTd&xNg$ ziE0TP8d9$5IGeF|W3(P#0u|e;UBuqv)h6h+q7wM=+?OLi;wN;OvR) zqFdE=IrYu4xf)ZZLUVoL)2{xkzOVN*>1)58!5C^iSH=GH-?;EVW9==^VNtRc1j`P= zWDY;+8&Qwi>a3tTRYK)sT%Lwo9kDe)-bcAgU3*D+`LKM>##%0+@%PyETWe3xx@_Ro zKj>RLlIt8J2FdV8Pd`j=lA?3PgmGejz4WbGZ~AR#aN%DVj%6noh__z%(gAHt13vXfDcZ%$b3plfI1vR4;<(@)QXgUQ^}rr|@=6dq zeQQ!ttK4RJ|8r?I{y2PfZ~&;YUJFs^0bscr=2~37`y+Q$aeQ$xp?Ie|w|58k=)*+p z2^-4AhE~>hId;HeoD5BB&D`0kNIGXin1_fuCYX!8Le7B!mYK9>tp=A{mg-R!XYwZ0 zY7?npukrv(NXyah2yLX^wd3XeS)B@Bd(l_Tr^}VeQf&JYq8fOkTDS=F6zxU4?@v!~ z)Q;x6jttt=SD=3H+n~X>AQB2!7=sM$WRrcF;zqf$Y4MON3cQTk!$#f;o|E0Qlka=Q z@1;1(b}?Uaexrp9d=q8;^G#)U2NzU*<=9VrvgF0lPy&bi!;Ry>bYt7n+uD%On?dU? zpNtR|8@DnFjvJR2#D!6Hb@9P=b57PTP%+D+cKbosIPgBbP-ItQ>ciMiaui9B7qudF z-{OZ&2piM$Bd!W64OqUp;I3_^4NVC#2KJ21xc5-L4zYM@vRI{K8pPfa*J~`xulj_< zU@NV=N^=EfYJM@F%}*>$d~ub3A59dp;9&h0NJPe5W7Xe2kM340DI81|WxMS3JU&C~ zd99TQ4cgO)ulcpcR~BQ+A5Ly@!`FYdt(8mJVY||qM)eKvO*i%?@OV1^UayPYj$6e3 z?do>SfQ^5T`&*A?$&?-ch5M9ZEmdt#+Dq=_SQW7ybHa?8JfJF%6h**gsVMLd7j`*xw+Y^5_A zc5dt@X$Fag^>d$JP+14u($%?$cKpz{MuBy{h@&S+!e)!Obe0Ewwxbl`Z{NpwU5R_k zrtc4onqPsE&(^e27>-AN1YaOAQ!0biqrKIN4hIvfyO!dGAeiMnP$i5|wlckK94M&kN(>AC?29{AE7yyX23Q{))gFG1Jv@`yD`arV!mj`+UqN1zoh z>R8nmhmz9WzRUkG;n3{qj#f}LiZqeLfUu(U_Hnj|W3)1sgqjV60W@-T6GJ}UEi=mZ zr25GspeOHZ=_?AWAXGoGz;0Ik48X9{fWAwu;#-inkk8-T7;&C+x}GV)hB)A%fh~;k*boW z%6Y)+YmwK`?#|27MyYrSyKdc2!B-{){XK(BRQ7?v*<@w~7!}y+lwMXLD~qyTN@c>( zMX6MMjaGUA9sWYEOs>s2@YhhYH-k&o) zc3Q-8zOylFASU@E9*&l|Nkgkkg}()g{n>3y$2#wi1r-O3?QF{v_%$cigx3~e?>ch-=f47$eG|{Q&s5wpcJ8};QXOHVnHSC#!#V%Y-LwJJIUeN~P}patNQ8x)fz4!s zz)Y(!u5#lgI%R|u&6;BIL?0Z?V{kBz=4RY1sQ;-8POLz2AU{EaM-Gc1aFscw)yFy; ze%o+n3(E)}<*O{HZg=@at|Vlo(!99=6$(p5d>5u%)V*Hn;I=s(UJ1pIOY9U&S0KZh zyB}A)0bkz59DIXoGG^1}O!Hx|ij}YB;Gb8;cM@zg@GiHM}m(%*!7Ca`(O;={T_htK#W%a~OB7NZlvho4#WEAlW5#K3v$!yM0;b0;1 zCig1%z}M5F;ET_vvZx}k=IyB!#P?+o-zVzw>5~_D--cx`LGsNsU!WER-myslPLD!Z z)H;JvZg^xV3y^{IXC5?ituExw{o`x`&)Pjk6j<}0nfOI0C7vqNkQI+%DNhIIT36T&C{O5*Kn2>=El65Sn2Xuz*ji z{g#Mf1n8lm4?sVr84O}#X^kgBXL417YE%IEGZB2IuEZ*F9un6THxr}e{HWnUsaDQ( zuw_`oS@;&jTZxLZ1#MDOSKQ^!uZ=vNyh`&2g{=4bW$g2nkw;WFn zU_4)+OJo%v8V&%%sony8dGf`{XIt-PfLFb_6S228JP*2fIsFxlVE#-PouVxO@YsEM zb6--raJn80eQE|@XG-A_5qkSs2(EZKQDHU@oEqPWw3Z-1<|TlT%VF%#_%K5vfgCI_w^^La2y!2@9 zs}FpqEKHU_`-WM#tD+Iz;Kqk;ITXzgRYp=VaDe2>7XVqE16`~w z;!OGj;4^p!x>U`;{PONPPe>P0vM5>yk7o+jN??!|Z6>i9M#8-TGxAjLyg@e60*-PE z{A!K85WL)oQYT2WO+Vq)SeS)KbDV?na1Ja9FdHC_mh0}~WT^qWM`0)^ivkGW3L^g6 z1Odd&yZpe}{xh=75smZkO%EWC=hR7m2CiJd?Op;V_<(~Em2Cn*d5Qa%wE#2XQzfCA z-L^v@9W?_yGBJBLG8YS)MolWfh2#G;XA|=RC=qW#Q+`10`1|uM(7I>}S)3)w!x%%7 zWo`$5iwX4N$Mv4V(V`K%_h!+Er{IJv5yUvn1X)GMQJ_gVhl!yN=4AmE2G7^(P*qZA zryEBfBXN--=VD6n5^`u$kqG%31>(5;nUoT##Tb%SNsOB@4B zV%&L@OYOnB3Wi$wfc>f7Cbm`|dI>_hkEsBV;9M87Dw<4gdukwB8 zqFLBPd-h%z{xd{mY!BCL67L%=;Jz{7QO&bdiUgNfUt1{|(q5hzt!GyfznjSM0&sA8Hssjs!Zedu+V{G=0fx1a@Q-K&ngY;X|6dNeFtX^Q z{60Nh-iBlYW-Go@(7jgfeQbbhmL(qm?rnR`4Z<;aTK6(@tdMBFe7&n{^Ub&$I0#e~ zIEaRt@X+;JbT}@D^~1p)d&` zMmk^@ju`^x588oXqNT}SIR8N^mZVKrTE$X4s;BO>mW2M`mK4rWq9GyaskOL0X;Trs z0+?t35w3QhAX&snA`%|fr9x5d9RZ}Z{@hMNw-bwk?4C9E80N7r*SGDG(GG3IKKzqL zjSuD-2J>zr{@q(cBLcxy4DR!sCNZoc?5HD$)Fnuk9RZ?3>UCXkv8N!)LVP1zX#;w< zeK|Ay3({8R`rt=)r-XZQs$P*p#$&&ADq)C^@0L0OPV11SGkyL~nsL7HlWGbS<3+NW zXg!rzf_dCAt{T;{q`fCmc&ph}2+|PYq=Xssic?r*^ zzq%XIwv84_;D=fp=*ejcPuZj#bET0(d7(~Y$79FkIz@t#3T9C|!A7|4yT-*Q$ank{+ z#zJg@{J0qyxHj4kK+DP3N!;Hh)xTTmm`tEScl1=(-w;2|2Rm?vx`aua>Ro4dBd-^= z?LiU>8ebYpD6VCy=AiJc$y&?3@f$kZV^ft=tzb+=tdoP+iVOh!S0L|2%5V z`8Yi+f`K5#v`S zeCX?xlolejM9l7fwZ2g|XXHUk%`59)mm@}T zs~mq3uy@iG>>Xr?W<)vq#fMwHhK>haVdBqjl?!;OIK77bGqH1_0SpgXCnzYY;nJ@* z-8LaDh^#sVmfHj#;hBn=P|LyyHl7`KT$rWR?`^v3`M62^mJ0#5 zjq@xQEhxGhGhlE5*;&uxQy?y0o^vZC7RE`&Qp%va$rfc|Wb%46ErKZtZNXl+m?`PF zM)!yk+v7`nukZ53P@QM)RxfZC9(#Y&#HM+Qvy@%ywZnfkTav}t%KmG|ur;W|s3fb6 z`@$l*vu+w5Ybb9;%2&NSXS9!p?0d3d?*7VMt#^^}G8`vr8hUMr9A}jM(?(;{7upN? z;lGN7SgLd5 zGn?EKKMLs} zA+ih^Lx~d}!iCS8Gz9dha_pj}`oKd;54s$R$ab68xpQPFn!2c4Hyp+GjosX>zs%l< z<=(71ZcaO~ozDtpinGxav>Pod+ORfQQ~+I3CS4UMWA3$S!yz9=;fYfIqvRhk;74`R|bM&j7EIK zrwgJ^?Zu)AHkX-!=BY$!iLE#b>C?jnCO7s_=1i&DVjuPRHy36xd;NO!WJu<+X|ftG zI=P&vsJ<_z#*ey@HT)EU$PkmiJYaR!>u1f zsq$UELYbuvYJW>WJ2JK(S|!aAm9|U_hj%ZGeDh+`#Brttun$Eb^@aswAHHU$ zC8;z?lyCSZG$YrmCNDnIZ3PSUTKvVriz{lBs$%L>O11n?tx7aSSICEs8JTrp9=5N( ztD@#DYO$3kf4t(`iv3v;9rn(X(iGJt`IQvaa7le3lC>@@7cT}$Kh6M#_?2WEtG$}S3G&y| zBeAx^B~Cc`w<3f~2z=W=SQqZz31CfbXISf09Uw~ga^Aj4BXQ(?(|26(<*#@=(=U%Da zEfs;v{`u=gUu+?KWMQVVYC!8d0+Am# zHsi8O2q5@O@m5lWJ#d@f_biN=lmH-n4lU4Y5WZoW4-OaJ*1F(3Qtk`B9sZ4Sfw7f& zzY2fWifgwAZS4#V3@p|<{Pd+J_2(qW`8vwjPj~rskpn;JWI&8t-_M0_y5=18)G+v` zC$BnusRa*?(+tOXuvIN~OvyA!G-3rqY`_P@iSI}!8iKBjmb_E-h04!>NCg1@PP+Cb z80OW1SfdmSOcM7Xa3lomJOo#@s|MNThzdXN1ymzc)wxDz9ynks28@ffB{tfwOY%g{ zlV<_YQ2jDF`+iSOey{^zIPA|@xgGC@;ld-(p`rvg2Xp)dk(;>fK>V4VXGR2)!O0iq zPJ}!`i*tdPn3p)6DZaf%sPq-{K1|4G#8YBgmWdejmO@LMa*+Bv5fHi{HXM8rM?n1= z=;U1x1^t^6A{_2+a>)(*E+vHJc3%nsa3_GrKYF&~gxWlTwiBubp5(u}sdl`fmNr^3 zd2n7KI^_2hPXNfh7dn#Uw%o4-v_1mkWS=zHT=nyvMIO{iL5HYkx+RMA(~ulKB}^r+ z^y5Bp+D2Gy9R$VX0 zu+Swc@_(@YlqF$LXsOYP;X|nB;A2_}na0mc3SmFv96Q)|uv%1DO#tD!TqD(#ms4)ZnUkEuSxT zODGLR&@?Q)x4;lb0KJTirZp`%XoZ7}zuA8S^!1`{f)`kvGd^l9Xo(u?HoX^o7 z799D2rP6LjhwlV9FY$xEm_6z;#&5P=aSm}a+BH@q zB!FrTxe*;qNx|VL7X+L8>d>zxklYiJ&-bs*X`}fK!?XU2AXOc8Ec&GXe0TiN=aT%g z>5=Fs(jMVD8q=?6#33a*$FQUhPSajz+4Jeks?=6KDC@(J$hUR64%*3Q zB~)hW$&pwLr%{BpbkQ5QOPU)+)m@@;_x1yX9x!bL}ml|1E|al6L+I=^B_4aSM3gVDuGm&E@a zt1HhE+A;Bicn!zxxHUYfn%8Gy?*)+}zxbbE6j7uMFGa1eWY_p>joXmzHnEH+Ve8{I zD6-54PC69b!4$xGDcyYRZHjs(xs2R|i?ck6G6K9_k2d{EQMO%|W6ymZhT!!D?<${^ zCkcYZpB&nw4v8CtH)vMm9X0nVW<6}q`|>1shqP%Kd8&QE{f7rb-#8KN?miDb0`z(| zgi_h}x!*T#5DQ|thg6$yfk&(wDA4mFS>Hs?`2a`#EIBb@qZomw`YR-WsL+2Oolb65 zhyVMO{-M4QawX=APvDjp*LjIhQy6d>e`uk4DV>MEXgZWB>$-szGv4FrNhSKg_1-<} zxu1{7)d0%~aGS|X`*DtztpOXBqND4;rW9t22 vI@uB#eb!g5$AonX->kmxZTcpteO(g$ply9zpt`mj1wZ>u5AMw~rbqq{mxGj> From 4e23d47ba2a42e85bf8f483420e575d828ce19ad Mon Sep 17 00:00:00 2001 From: Hunter Heston Date: Thu, 12 Dec 2024 10:14:35 -0800 Subject: [PATCH 071/562] feat(js/plugins/checks): Add Checks Guardrails middleware (#1451) --- js/plugins/checks/README.md | 149 +++++++++++++++++++++++----- js/plugins/checks/package.json | 2 + js/plugins/checks/src/evaluation.ts | 51 +--------- js/plugins/checks/src/guardrails.ts | 104 +++++++++++++++++++ js/plugins/checks/src/index.ts | 93 +++++++++++------ js/plugins/checks/src/metrics.ts | 61 ++++++++++++ js/plugins/checks/src/middleware.ts | 79 +++++++++++++++ js/pnpm-lock.yaml | 17 ++++ 8 files changed, 454 insertions(+), 102 deletions(-) create mode 100644 js/plugins/checks/src/guardrails.ts create mode 100644 js/plugins/checks/src/metrics.ts create mode 100644 js/plugins/checks/src/middleware.ts diff --git a/js/plugins/checks/README.md b/js/plugins/checks/README.md index 9b26f2fab..a68e3fe51 100644 --- a/js/plugins/checks/README.md +++ b/js/plugins/checks/README.md @@ -1,12 +1,14 @@ # Checks -Checks is an AI safety platform built by Google: [checks.google.com/ai-safety](https://checks.google.com/ai-safety). +**Checks** is an AI safety platform built by Google: [checks.google.com/ai-safety](https://checks.google.com/ai-safety). -This plugin provides evaluators for each Checks AI safety policy. Text is cassified by calling the [Checks Guardrails API](https://console.cloud.google.com/marketplace/product/google/checks.googleapis.com). +This plugin provides evaluators and middleware for each Checks AI safety policy. Text is classified by calling the [Checks Guardrails API](https://console.cloud.google.com/marketplace/product/google/checks.googleapis.com?project=_). -> Note: The Guardrails is currently in private preview and you will need to request quota. To request quota fill out this [Google form](https://docs.google.com/forms/d/e/1FAIpQLSdcLZkOJMiqodS8KSG1bg0-jAgtE9W-AludMbArCKqgz99OCA/viewform?usp=sf_link) +> **Note**: The Guardrails API is currently in private preview, and you will need to request quota. To request quota, fill out this [Google form](https://docs.google.com/forms/d/e/1FAIpQLSdcLZkOJMiqodS8KSG1bg0-jAgtE9W-AludMbArCKqgz99OCA/viewform?usp=sf_link). +> +> Checks also provides an Automated Adversarial Testing solution for offline evaluations of your model, where you can configure your policies just like in the Guardrails API, and Checks will create adversarial prompts to test your model and report which prompts and policies may need further investigation. You can use the same form to express interest in the Automated Adversarial Testing product. -Curently that list includes: +Currently, the supported policies include: ```text DANGEROUS_CONTENT @@ -19,11 +21,115 @@ VIOLENCE_AND_GORE OBSCENITY_AND_PROFANITY ``` -## How to use +## Middleware -### Configure the plugin +If you include Checks middleware in any of your flows, it will use the Guardrails API to test any model input and output for policy violations. -Add the `checks` plugin to your Genkit entrypoint and configured the evaluators you want to use: +### How to Use + +#### Example + +```ts +// Import the checks middleware and metric types. +import { + checksMiddleware, + ChecksEvaluationMetricType, +} from '@genkit-ai/checks'; + +// Import any models you would like to use. +import { gemini15Flash, googleAI } from '@genkit-ai/googleai'; + +export const ai = genkit({ + plugins: [ + googleAI(), + // ... any other plugins you need. + ], +}); + +// Simple example flow +export const poemFlow = ai.defineFlow( + { + name: 'menuSuggestionFlow', + }, + async (topic) => { + const { text } = await ai.generate({ + model: gemini15Flash, + prompt: `Write a poem on this topic: ${topic}`, + // Add checks middleware to your generate calls. + use: [ + checksMiddleware({ + authOptions: { + // Project to charge quota to. + // Note: If your credentials have a quota project associated with them, + // that value will take precedence over this. + projectId: 'your-project-id', + }, + // Add the metrics/policies you want to validate against. + metrics: [ + // This will use the default threshold determined by Checks. + ChecksEvaluationMetricType.DANGEROUS_CONTENT, + // This is how you can override the default threshold. + { + type: ChecksEvaluationMetricType.VIOLENCE_AND_GORE, + // If the content scores above 0.55, it fails and the response will be blocked. + threshold: 0.55, + }, + ], + }), + ], + }); + return text; + } +); +``` + +#### Test Your Flow + +Start the UI: + +```bash +cd your/project/directory +genkit ui:start +``` + +#### Run Your App + +```bash +genkit start -- tsx --watch src/index.ts +``` + +#### Run the Flow + +Open your app, most likely at `localhost:4000`. Select **Flows** in the left navigation menu. Select your flow and provide input. Run it with benign and malicious input to verify that dangerous content is being blocked. + +#### Tune Thresholds + +You can raise or lower the thresholds for each individual policy to match your application's needs. A lower threshold will block more content, while a higher threshold will allow more content through. + +For example: + +```ts +{ + // This policy has a very low threshold, so any content that is even slightly violent will be blocked. + type: ChecksEvaluationMetricType.VIOLENCE_AND_GORE, + threshold: 0.01, +}, +{ + // This policy has a very high threshold, so almost nothing will be blocked, even if the content contains some dangerous content. + type: ChecksEvaluationMetricType.DANGEROUS_CONTENT, + threshold: 0.99, +} +``` + +## Evaluator + +The Checks evaluators let you run offline safety tests of prompts. + +### How to Use + +#### Configure the Plugin + +Add the `checks` plugin to your Genkit entry point and configure the evaluators you want to use: ```ts import { checks, ChecksEvaluationMetricType } from '@genkit-ai/checks'; @@ -32,18 +138,18 @@ export const ai = genkit({ plugins: [ checks({ // Project to charge quota to. - // Note: If your credentials have a quota project associated with them. - // That value will take precedence over this. + // Note: If your credentials have a quota project associated with them, + // that value will take precedence over this. projectId: 'your-project-id', evaluation: { metrics: [ - // Policies configured with the default threshold(0.5). + // Policies configured with the default threshold (0.5). ChecksEvaluationMetricType.DANGEROUS_CONTENT, ChecksEvaluationMetricType.HARASSMENT, ChecksEvaluationMetricType.HATE_SPEECH, ChecksEvaluationMetricType.MEDICAL_INFO, ChecksEvaluationMetricType.OBSCENITY_AND_PROFANITY, - // Policies configured with non-default threshold. + // Policies configured with non-default thresholds. { type: ChecksEvaluationMetricType.PII_SOLICITING_RECITING, threshold: 0.6, @@ -63,36 +169,33 @@ export const ai = genkit({ }); ``` -### Create a test dataset +#### Create a Test Dataset Create a JSON file with the data you want to test. Add as many test cases as you want. `output` is the text that will be classified. -```JSON +```json [ { "testCaseId": "test_case_id_1", "input": "The input to your model.", - "output": "Example model output which. This is what will be evaluated." + "output": "Example model output. This is what will be evaluated." } ] - ``` -### Run the evaluator +#### Run the Evaluators ```bash -# Run all configured classifiers. +# Run the configured evaluators. genkit eval:run test-dataset.json --evaluators=checks/guardrails ``` -### View the results +#### View the Results -Run `genkit start -- tsx --watch src/index.ts` and open the genkit ui. Usually at `localhost:4000` and select the Evaluate tab. +Run `genkit start -- tsx --watch src/index.ts` and open the Genkit UI, usually at `localhost:4000`. Select the **Evaluate** tab. # Genkit -The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. - -Usage information and reference details can be found in [Genkit documentation](https://firebase.google.com/docs/genkit). +The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repository. Please file issues and pull requests against that repository. -License: Apache 2.0 +Usage information and reference details can be found in the [Genkit documentation](https://firebase.google.com/docs/genkit). diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index 8ee1e29c2..390357261 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -30,6 +30,8 @@ "author": "genkit", "license": "Apache-2.0", "dependencies": { + "@genkit-ai/ai": "workspace:*", + "@googleapis/checks": "^4.0.2", "google-auth-library": "^9.6.3" }, "peerDependencies": { diff --git a/js/plugins/checks/src/evaluation.ts b/js/plugins/checks/src/evaluation.ts index bd1fd5df9..6970308ba 100644 --- a/js/plugins/checks/src/evaluation.ts +++ b/js/plugins/checks/src/evaluation.ts @@ -18,46 +18,11 @@ import { EvaluatorAction, Genkit, z } from 'genkit'; import { BaseEvalDataPoint } from 'genkit/evaluator'; import { runInNewSpan } from 'genkit/tracing'; import { GoogleAuth } from 'google-auth-library'; - -/** - * Currently supported Checks AI Safety policies. - */ -export enum ChecksEvaluationMetricType { - // The model facilitates, promotes or enables access to harmful goods, - // services, and activities. - DANGEROUS_CONTENT = 'DANGEROUS_CONTENT', - // The model reveals an individual’s personal information and data. - PII_SOLICITING_RECITING = 'PII_SOLICITING_RECITING', - // The model generates content that is malicious, intimidating, bullying, or - // abusive towards another individual. - HARASSMENT = 'HARASSMENT', - // The model generates content that is sexually explicit in nature. - SEXUALLY_EXPLICIT = 'SEXUALLY_EXPLICIT', - // The model promotes violence, hatred, discrimination on the basis of race, - // religion, etc. - HATE_SPEECH = 'HATE_SPEECH', - // The model facilitates harm by providing health advice or guidance. - MEDICAL_INFO = 'MEDICAL_INFO', - // The model generates content that contains gratuitous, realistic - // descriptions of violence or gore. - VIOLENCE_AND_GORE = 'VIOLENCE_AND_GORE', - // The model generates content that contains vulgar, profane, or offensive - // language. - OBSCENITY_AND_PROFANITY = 'OBSCENITY_AND_PROFANITY', -} - -/** - * Checks evaluation metric config. Use `threshold` to override the default violation threshold. - * The value of `metricSpec` will be included in the request to the API. See the API documentation - */ -export type ChecksEvaluationMetricConfig = { - type: ChecksEvaluationMetricType; - threshold?: number; -}; - -export type ChecksEvaluationMetric = - | ChecksEvaluationMetricType - | ChecksEvaluationMetricConfig; +import { + ChecksEvaluationMetric, + ChecksEvaluationMetricConfig, + isConfig, +} from './metrics'; export function checksEvaluators( ai: Genkit, @@ -80,12 +45,6 @@ export function checksEvaluators( return createPolicyEvaluator(projectId, auth, ai, policy_configs); } -function isConfig( - config: ChecksEvaluationMetric -): config is ChecksEvaluationMetricConfig { - return (config as ChecksEvaluationMetricConfig).type !== undefined; -} - const ResponseSchema = z.object({ policyResults: z.array( z.object({ diff --git a/js/plugins/checks/src/guardrails.ts b/js/plugins/checks/src/guardrails.ts new file mode 100644 index 000000000..af595fe56 --- /dev/null +++ b/js/plugins/checks/src/guardrails.ts @@ -0,0 +1,104 @@ +/** + * Copyright 2024 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 { z } from 'genkit'; +import { GoogleAuth } from 'google-auth-library'; +import { ChecksEvaluationMetric, isConfig } from './metrics'; + +const GUARDRAILS_URL = + 'https://checks.googleapis.com/v1alpha/aisafety:classifyContent'; + +/** + * Request to the Checks Guardrails ClisifyContent endpoint. + */ +type GuardrailsRequest = { + input: { + text_input: { + content: string; + }; + }; + policies: { + policy_type: string; + threshold?: number; + }[]; +}; + +/** + * Response type returned by the classifyContent endpoint. + */ +const ResponseSchema = z.object({ + policyResults: z.array( + z.object({ + policyType: z.string(), + score: z.number().optional(), + violationResult: z.string(), + }) + ), +}); + +type ResponseType = z.infer; + +/** + * API implementation for making requests to the guardrails api. + */ +export class Guardrails { + private auth: GoogleAuth; + private projectId: string | undefined; + + constructor(auth: GoogleAuth, projectId?: string) { + this.auth = auth; + this.projectId = projectId; + } + + async classifyContent( + content: string, + policies: ChecksEvaluationMetric[] + ): Promise { + const body: GuardrailsRequest = { + input: { + text_input: { + content: content, + }, + }, + policies: policies.map((policy) => { + const policyType = isConfig(policy) ? policy.type : policy; + const threshold = isConfig(policy) ? policy.threshold : undefined; + + return { + policy_type: policyType, + threshold: threshold, + }; + }), + }; + + const client = await this.auth.getClient(); + const response = await client.request({ + url: GUARDRAILS_URL, + method: 'POST', + body: JSON.stringify(body), + headers: { + 'x-goog-user-project': this.projectId, + 'Content-Type': 'application/json', + }, + }); + + try { + return ResponseSchema.parse(response.data); + } catch (e) { + throw new Error(`Error parsing ${GUARDRAILS_URL} API response: ${e}`); + } + } +} diff --git a/js/plugins/checks/src/index.ts b/js/plugins/checks/src/index.ts index 103830a92..cd1a96434 100644 --- a/js/plugins/checks/src/index.ts +++ b/js/plugins/checks/src/index.ts @@ -18,18 +18,20 @@ import { Genkit } from 'genkit'; import { logger } from 'genkit/logging'; import { GenkitPlugin, genkitPlugin } from 'genkit/plugin'; import { GoogleAuth, GoogleAuthOptions } from 'google-auth-library'; +import { checksEvaluators } from './evaluation.js'; import { ChecksEvaluationMetric, ChecksEvaluationMetricType, - checksEvaluators, -} from './evaluation.js'; +} from './metrics.js'; +import { checksMiddleware as authorizedMiddleware } from './middleware.js'; + export { ChecksEvaluationMetricType as ChecksEvaluationMetricType }; export interface PluginOptions { /** The Google Cloud project id to call. Must have quota for the Checks API. */ projectId?: string; /** Provide custom authentication configuration for connecting to Checks API. */ - googleAuth?: GoogleAuthOptions; + googleAuthOptions?: GoogleAuthOptions; /** Configure Checks evaluators. */ evaluation?: { metrics: ChecksEvaluationMetric[]; @@ -46,36 +48,9 @@ const CHECKS_OAUTH_SCOPE = 'https://www.googleapis.com/auth/checks'; */ export function checks(options?: PluginOptions): GenkitPlugin { return genkitPlugin('checks', async (ai: Genkit) => { - let authClient: GoogleAuth; - let authOptions = options?.googleAuth; - - // Allow customers to pass in cloud credentials from environment variables - // following: https://github.com/googleapis/google-auth-library-nodejs?tab=readme-ov-file#loading-credentials-from-environment-variables - if (process.env.GCLOUD_SERVICE_ACCOUNT_CREDS) { - const serviceAccountCreds = JSON.parse( - process.env.GCLOUD_SERVICE_ACCOUNT_CREDS - ); - authOptions = { - credentials: serviceAccountCreds, - scopes: [CLOUD_PLATFROM_OAUTH_SCOPE, CHECKS_OAUTH_SCOPE], - }; - authClient = new GoogleAuth(authOptions); - } else { - authClient = new GoogleAuth( - authOptions ?? { - scopes: [CLOUD_PLATFROM_OAUTH_SCOPE, CHECKS_OAUTH_SCOPE], - } - ); - } - - const client = await authClient.getClient(); - if (client.quotaProjectId) { - logger.warn( - `Checks Evaluator: Your Google cloud authentication has a default quota project(${client.quotaProjectId}) associated with it which will overrid the projectId in your Checks plugin config(${options?.projectId}).` - ); - } + const googleAuth = inititializeAuth(options?.googleAuthOptions); - const projectId = options?.projectId || (await authClient.getProjectId()); + const projectId = options?.projectId || (await googleAuth.getProjectId()); if (!projectId) { throw new Error( @@ -87,8 +62,60 @@ export function checks(options?: PluginOptions): GenkitPlugin { options?.evaluation && options.evaluation.metrics.length > 0 ? options.evaluation.metrics : []; - checksEvaluators(ai, authClient, metrics, projectId); + checksEvaluators(ai, googleAuth, metrics, projectId); + }); +} + +export function checksMiddleware(options: { + authOptions: GoogleAuthOptions; + metrics: ChecksEvaluationMetric[]; +}) { + const googleAuth = inititializeAuth(options.authOptions); + + return authorizedMiddleware({ + auth: googleAuth, + metrics: options.metrics, + projectId: options.authOptions.projectId, }); } +/** + * Helper function for initializing an instance of GoogleAuth. + * + * @param options Options for initializing a GoogleAuth instance. + * @returns GoogleAuth + */ +function inititializeAuth(options?: GoogleAuthOptions): GoogleAuth { + let googleAuth: GoogleAuth; + + // Allow customers to pass in cloud credentials from environment variables + // following: https://github.com/googleapis/google-auth-library-nodejs?tab=readme-ov-file#loading-credentials-from-environment-variables + if (process.env.GCLOUD_SERVICE_ACCOUNT_CREDS) { + const serviceAccountCreds = JSON.parse( + process.env.GCLOUD_SERVICE_ACCOUNT_CREDS + ); + options = { + credentials: serviceAccountCreds, + scopes: [CLOUD_PLATFROM_OAUTH_SCOPE, CHECKS_OAUTH_SCOPE], + }; + googleAuth = new GoogleAuth(options); + } else { + googleAuth = new GoogleAuth( + options ?? { + scopes: [CLOUD_PLATFROM_OAUTH_SCOPE, CHECKS_OAUTH_SCOPE], + } + ); + } + + googleAuth.getClient().then((client) => { + if (client.quotaProjectId && options?.projectId) { + logger.warn( + `Checks Evaluator: Your Google cloud authentication has a default quota project(${client.quotaProjectId}) associated with it which will overrid the projectId in your Checks plugin config(${options?.projectId}).` + ); + } + }); + + return googleAuth; +} + export default checks; diff --git a/js/plugins/checks/src/metrics.ts b/js/plugins/checks/src/metrics.ts new file mode 100644 index 000000000..9e23387bb --- /dev/null +++ b/js/plugins/checks/src/metrics.ts @@ -0,0 +1,61 @@ +/** + * Copyright 2024 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. + */ + +/** + * Currently supported Checks AI Safety policies. + */ +export enum ChecksEvaluationMetricType { + // The model facilitates, promotes or enables access to harmful goods, + // services, and activities. + DANGEROUS_CONTENT = 'DANGEROUS_CONTENT', + // The model reveals an individual’s personal information and data. + PII_SOLICITING_RECITING = 'PII_SOLICITING_RECITING', + // The model generates content that is malicious, intimidating, bullying, or + // abusive towards another individual. + HARASSMENT = 'HARASSMENT', + // The model generates content that is sexually explicit in nature. + SEXUALLY_EXPLICIT = 'SEXUALLY_EXPLICIT', + // The model promotes violence, hatred, discrimination on the basis of race, + // religion, etc. + HATE_SPEECH = 'HATE_SPEECH', + // The model facilitates harm by providing health advice or guidance. + MEDICAL_INFO = 'MEDICAL_INFO', + // The model generates content that contains gratuitous, realistic + // descriptions of violence or gore. + VIOLENCE_AND_GORE = 'VIOLENCE_AND_GORE', + // The model generates content that contains vulgar, profane, or offensive + // language. + OBSCENITY_AND_PROFANITY = 'OBSCENITY_AND_PROFANITY', +} + +/** + * Checks evaluation metric config. Use `threshold` to override the default violation threshold. + * The value of `metricSpec` will be included in the request to the API. See the API documentation + */ +export type ChecksEvaluationMetricConfig = { + type: ChecksEvaluationMetricType; + threshold?: number; +}; + +export type ChecksEvaluationMetric = + | ChecksEvaluationMetricType + | ChecksEvaluationMetricConfig; + +export function isConfig( + config: ChecksEvaluationMetric +): config is ChecksEvaluationMetricConfig { + return (config as ChecksEvaluationMetricConfig).type !== undefined; +} diff --git a/js/plugins/checks/src/middleware.ts b/js/plugins/checks/src/middleware.ts new file mode 100644 index 000000000..df87a31f4 --- /dev/null +++ b/js/plugins/checks/src/middleware.ts @@ -0,0 +1,79 @@ +/** + * Copyright 2024 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 { ModelMiddleware } from 'genkit/model'; +import { GoogleAuth } from 'google-auth-library'; +import { Guardrails } from './guardrails'; +import { ChecksEvaluationMetric } from './metrics'; + +export function checksMiddleware(options: { + auth: GoogleAuth; + metrics: ChecksEvaluationMetric[]; + projectId?: string; +}): ModelMiddleware { + const guardrails = new Guardrails(options.auth, options?.projectId); + + const classifyContent = async (content: string) => { + const response = await guardrails.classifyContent(content, options.metrics); + + // Filter for violations + const violatedPolicies = response.policyResults.filter( + (policy) => policy.violationResult === 'VIOLATIVE' + ); + + return violatedPolicies; + }; + + return async (req, next) => { + for (const message of req.messages) { + for (const content of message.content) { + if (content.text) { + const violatedPolicies = await classifyContent(content.text); + + // If any input message violates a checks policy. Stop processing, + // return a blocked response and list of violated policies. + if (violatedPolicies.length > 0) { + return { + finishReason: 'blocked', + finishMessage: `Model input violated Checks policies: [${violatedPolicies.map((result) => result.policyType).join(' ')}], further processing blocked.`, + }; + } + } + } + } + + const generatedContent = await next(req); + + for (const candidate of generatedContent.candidates ?? []) { + for (const content of candidate.message.content ?? []) { + if (content.text) { + const violatedPolicies = await classifyContent(content.text); + + // If the output message violates a checks policy. Stop processing, + // return a blocked response and list of violated policies. + if (violatedPolicies.length > 0) { + return { + finishReason: 'blocked', + finishMessage: `Model output violated Checks policies: [${violatedPolicies.map((result) => result.policyType).join(' ')}], output blocked.`, + }; + } + } + } + } + + return generatedContent; + }; +} diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index c1711a784..64b2cbe6d 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -228,6 +228,12 @@ importers: plugins/checks: dependencies: + '@genkit-ai/ai': + specifier: workspace:* + version: link:../../ai + '@googleapis/checks': + specifier: ^4.0.2 + version: 4.0.2(encoding@0.1.13) genkit: specifier: workspace:* version: link:../../genkit @@ -2242,6 +2248,10 @@ packages: resolution: {integrity: sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==} engines: {node: '>=18.0.0'} + '@googleapis/checks@4.0.2': + resolution: {integrity: sha512-YV6sX7o2pS7ZFYp5N2EyGgnvC8F+0Wxoo3Mx+DFF3PFOsCfOUFZE35/ZQBdUFY6l1L0hcyz1lJQIzwmM7TeiCg==} + engines: {node: '>=12.0.0'} + '@grpc/grpc-js@1.10.10': resolution: {integrity: sha512-HPa/K5NX6ahMoeBv15njAc/sfF4/jmiXLar9UlC2UfHFKZzsCVLc3wbe7+7qua7w9VPh2/L6EBxyAV7/E8Wftg==} engines: {node: '>=12.10.0'} @@ -7143,6 +7153,13 @@ snapshots: '@google/generative-ai@0.21.0': {} + '@googleapis/checks@4.0.2(encoding@0.1.13)': + dependencies: + googleapis-common: 7.2.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color + '@grpc/grpc-js@1.10.10': dependencies: '@grpc/proto-loader': 0.7.13 From 0f4df1b34ea6650a1cfb10ce6f7354640ec738f2 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 12 Dec 2024 16:58:09 -0500 Subject: [PATCH 072/562] feat: renamed generate(streamingCallback) to generate(onChunk) (#1509) --- js/ai/src/chat.ts | 20 +++++++++------ js/ai/src/generate.ts | 30 +++++++++++++++++++---- js/core/src/action.ts | 6 ++--- js/core/src/flow.ts | 3 ++- js/genkit/tests/generate_test.ts | 28 +++++++++++++++++++++ js/genkit/tests/helpers.ts | 5 +++- js/plugins/dotprompt/src/prompt.ts | 2 +- js/plugins/dotprompt/tests/prompt_test.ts | 8 +++--- js/plugins/express/tests/express_test.ts | 2 +- js/plugins/mcp/tests/mcp_test.ts | 17 +++++++++++++ js/testapps/express/src/index.ts | 2 +- 11 files changed, 98 insertions(+), 25 deletions(-) create mode 100644 js/plugins/mcp/tests/mcp_test.ts diff --git a/js/ai/src/chat.ts b/js/ai/src/chat.ts index c565b0045..f22b351ba 100644 --- a/js/ai/src/chat.ts +++ b/js/ai/src/chat.ts @@ -14,24 +14,25 @@ * limitations under the License. */ -import { z } from '@genkit-ai/core'; +import { StreamingCallback, z } from '@genkit-ai/core'; import { runInNewSpan } from '@genkit-ai/core/tracing'; import { - generate, GenerateOptions, GenerateResponse, - generateStream, + GenerateResponseChunk, GenerateStreamOptions, GenerateStreamResponse, GenerationCommonConfigSchema, MessageData, Part, + generate, + generateStream, } from './index.js'; import { BaseGenerateOptions, - runWithSession, Session, SessionStore, + runWithSession, } from './session'; export const MAIN_THREAD = 'main'; @@ -138,8 +139,10 @@ export class Chat { this.session.registry, { metadata: { name: 'send' } }, async () => { - let resolvedOptions; - let streamingCallback = undefined; + let resolvedOptions: ChatGenerateOptions; + let streamingCallback: + | StreamingCallback + | undefined = undefined; // string if (typeof options === 'string') { @@ -153,7 +156,8 @@ export class Chat { } as ChatGenerateOptions; } else { resolvedOptions = options as ChatGenerateOptions; - streamingCallback = resolvedOptions.streamingCallback; + streamingCallback = + resolvedOptions.onChunk ?? resolvedOptions.streamingCallback; } let request: GenerateOptions = { ...(await this.requestBase), @@ -162,7 +166,7 @@ export class Chat { }; let response = await generate(this.session.registry, { ...request, - streamingCallback, + onChunk: streamingCallback, }); this.requestBase = Promise.resolve({ ...(await this.requestBase), diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index 1478405bd..3d686d7fe 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -17,8 +17,9 @@ import { Action, GenkitError, - runWithStreamingCallback, StreamingCallback, + runWithStreamingCallback, + sentinelNoopStreamingCallback, z, } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; @@ -29,7 +30,7 @@ import { resolveFormat, resolveInstructions, } from './formats/index.js'; -import { generateHelper, GenerateUtilParamSchema } from './generate/action.js'; +import { GenerateUtilParamSchema, generateHelper } from './generate/action.js'; import { GenerateResponseChunk } from './generate/chunk.js'; import { GenerateResponse } from './generate/response.js'; import { Message } from './message.js'; @@ -43,7 +44,7 @@ import { resolveModel, } from './model.js'; import { ExecutablePrompt } from './prompt.js'; -import { resolveTools, ToolArgument, toToolDefinition } from './tool.js'; +import { ToolArgument, resolveTools, toToolDefinition } from './tool.js'; export { GenerateResponse, GenerateResponseChunk }; export interface OutputOptions { @@ -77,6 +78,12 @@ export interface GenerateOptions< /** When true, return tool calls for manual processing instead of automatically resolving them. */ returnToolRequests?: boolean; /** When provided, models supporting streaming will call the provided callback with chunks as generation progresses. */ + onChunk?: StreamingCallback; + /** + * When provided, models supporting streaming will call the provided callback with chunks as generation progresses. + * + * @deprecated use {@link onChunk} instead. + */ streamingCallback?: StreamingCallback; /** Middleware to be used with this model call. */ use?: ModelMiddleware[]; @@ -279,7 +286,7 @@ export async function generate< return await runWithStreamingCallback( registry, - resolvedOptions.streamingCallback, + stripNoop(resolvedOptions.onChunk ?? resolvedOptions.streamingCallback), async () => { const response = await generateHelper( registry, @@ -298,6 +305,19 @@ export async function generate< ); } +/** + * Check if the callback is a noop callback and return undefined -- downstream models + * expect undefined if no streaming is requested. + */ +function stripNoop( + callback: StreamingCallback | undefined +): StreamingCallback | undefined { + if (callback === sentinelNoopStreamingCallback) { + return undefined; + } + return callback; +} + function stripUndefinedOptions(input?: any): any { if (!input) return input; const copy = { ...input }; @@ -374,7 +394,7 @@ export async function generateStream< try { generate(registry, { ...options, - streamingCallback: (chunk) => { + onChunk: (chunk) => { firstChunkSent = true; provideNextChunk(chunk); ({ resolve: provideNextChunk, promise: nextChunk } = diff --git a/js/core/src/action.ts b/js/core/src/action.ts index 19c28784e..f038b6acb 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -377,7 +377,7 @@ export function defineAction< export type StreamingCallback = (chunk: T) => void; const streamingAlsKey = 'core.action.streamingCallback'; -const sentinelNoopCallback = () => null; +export const sentinelNoopStreamingCallback = () => null; /** * Executes provided function with streaming callback in async local storage which can be retrieved @@ -390,7 +390,7 @@ export function runWithStreamingCallback( ): O { return registry.asyncStore.run( streamingAlsKey, - streamingCallback || sentinelNoopCallback, + streamingCallback || sentinelNoopStreamingCallback, fn ); } @@ -403,7 +403,7 @@ export function getStreamingCallback( ): StreamingCallback | undefined { const cb = registry.asyncStore.getStore>(streamingAlsKey); - if (cb === sentinelNoopCallback) { + if (cb === sentinelNoopStreamingCallback) { return undefined; } return cb; diff --git a/js/core/src/flow.ts b/js/core/src/flow.ts index 7052180f0..7e9c3eccc 100644 --- a/js/core/src/flow.ts +++ b/js/core/src/flow.ts @@ -24,6 +24,7 @@ import { Action, ActionResult, defineAction, + sentinelNoopStreamingCallback, StreamingCallback, } from './action.js'; import { runWithContext } from './auth.js'; @@ -193,7 +194,7 @@ export class Flow< return await this.action.run(input, { context: opts.context, telemetryLabels: opts.labels, - onChunk: opts.onChunk ?? (() => {}), + onChunk: opts.onChunk ?? sentinelNoopStreamingCallback, }); } diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index f9bd69627..4db291519 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -177,6 +177,34 @@ describe('generate', () => { await response; }); }); + + it('passes the streaming callback to the model', async () => { + const model = defineEchoModel(ai); + const flow = ai.defineFlow('wrapper', async (_, streamingCallback) => { + const response = await ai.generate({ + model: model, + prompt: 'hi', + onChunk: console.log, + }); + return response.text; + }); + const text = await flow(); + assert.ok((model as any).__test__lastStreamingCallback); + }); + + it('strips out the noop streaming callback', async () => { + const model = defineEchoModel(ai); + const flow = ai.defineFlow('wrapper', async (_, streamingCallback) => { + const response = await ai.generate({ + model: model, + prompt: 'hi', + onChunk: streamingCallback, + }); + return response.text; + }); + const text = await flow(); + assert.ok(!(model as any).__test__lastStreamingCallback); + }); }); describe('config', () => { diff --git a/js/genkit/tests/helpers.ts b/js/genkit/tests/helpers.ts index 7de9e8ab3..f31662945 100644 --- a/js/genkit/tests/helpers.ts +++ b/js/genkit/tests/helpers.ts @@ -27,11 +27,13 @@ import { import { SessionData, SessionStore } from '../src/session'; export function defineEchoModel(ai: Genkit): ModelAction { - return ai.defineModel( + const model = ai.defineModel( { name: 'echoModel', }, async (request, streamingCallback) => { + (model as any).__test__lastRequest = request; + (model as any).__test__lastStreamingCallback = streamingCallback; if (streamingCallback) { await runAsync(() => { streamingCallback({ @@ -86,6 +88,7 @@ export function defineEchoModel(ai: Genkit): ModelAction { })); } ); + return model; } export function bonknessEvaluator(ai: Genkit) { diff --git a/js/plugins/dotprompt/src/prompt.ts b/js/plugins/dotprompt/src/prompt.ts index 5e55da350..0ecd9a4c6 100644 --- a/js/plugins/dotprompt/src/prompt.ts +++ b/js/plugins/dotprompt/src/prompt.ts @@ -251,7 +251,7 @@ export class Dotprompt implements PromptMetadata { jsonSchema: options.output?.jsonSchema || this.output?.jsonSchema, }, tools: (options.tools || []).concat(this.tools || []), - streamingCallback: options.streamingCallback, + onChunk: options.onChunk ?? options.streamingCallback, returnToolRequests: options.returnToolRequests, use: options.use, } as GenerateOptions; diff --git a/js/plugins/dotprompt/tests/prompt_test.ts b/js/plugins/dotprompt/tests/prompt_test.ts index 0e6d31e1a..90cae459c 100644 --- a/js/plugins/dotprompt/tests/prompt_test.ts +++ b/js/plugins/dotprompt/tests/prompt_test.ts @@ -132,11 +132,11 @@ describe('Prompt', () => { const rendered = prompt.render({ input: { name: 'Michael' }, - streamingCallback, + onChunk: streamingCallback, returnToolRequests: true, use: middleware, }); - assert.strictEqual(rendered.streamingCallback, streamingCallback); + assert.strictEqual(rendered.onChunk, streamingCallback); assert.strictEqual(rendered.returnToolRequests, true); assert.strictEqual(rendered.use, middleware); }); @@ -447,13 +447,13 @@ describe('DotpromptRef', () => { const streamingCallback = (chunk) => console.log(chunk); const options = { input: { name: 'Charlie' }, - streamingCallback, + onChunk: streamingCallback, returnToolRequests: true, }; const rendered = await ref.render(registry, options); - assert.strictEqual(rendered.streamingCallback, streamingCallback); + assert.strictEqual(rendered.onChunk, streamingCallback); assert.strictEqual(rendered.returnToolRequests, true); }); diff --git a/js/plugins/express/tests/express_test.ts b/js/plugins/express/tests/express_test.ts index e3341c382..d3ebebde0 100644 --- a/js/plugins/express/tests/express_test.ts +++ b/js/plugins/express/tests/express_test.ts @@ -65,7 +65,7 @@ describe('telemetry', async () => { const { text } = await ai.generate({ model: 'echoModel', prompt: input.question, - streamingCallback: sendChunk, + onChunk: sendChunk, }); return text; } diff --git a/js/plugins/mcp/tests/mcp_test.ts b/js/plugins/mcp/tests/mcp_test.ts new file mode 100644 index 000000000..0849c3e82 --- /dev/null +++ b/js/plugins/mcp/tests/mcp_test.ts @@ -0,0 +1,17 @@ +/** + * Copyright 2024 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. + */ + +// no tests... :( diff --git a/js/testapps/express/src/index.ts b/js/testapps/express/src/index.ts index 288b62e07..3d06255f5 100644 --- a/js/testapps/express/src/index.ts +++ b/js/testapps/express/src/index.ts @@ -121,7 +121,7 @@ app.get('/jokeStream', async (req: Request, res: Response) => { config: { temperature: 1, }, - streamingCallback: (c) => { + onChunk: (c) => { res.write(c.content[0].text); }, }); From 8e86d6bd791b5f89d6b7d31351e6582220c4cb5f Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 12 Dec 2024 17:07:38 -0500 Subject: [PATCH 073/562] feat: turned flow second param into a destructurable side channel object (backwards compatible) (#1504) --- js/core/src/action.ts | 4 +- js/core/src/{auth.ts => context.ts} | 10 +-- js/core/src/flow.ts | 34 +++++++-- js/core/src/index.ts | 2 +- js/core/tests/flow_test.ts | 93 ++++++++++++++++++++++-- js/plugins/express/tests/express_test.ts | 5 +- 6 files changed, 127 insertions(+), 21 deletions(-) rename js/core/src/{auth.ts => context.ts} (94%) diff --git a/js/core/src/action.ts b/js/core/src/action.ts index f038b6acb..f5120e343 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -16,6 +16,7 @@ import { JSONSchema7 } from 'json-schema'; import * as z from 'zod'; +import { getContext } from './context.js'; import { ActionType, Registry } from './registry.js'; import { parseSchema } from './schema.js'; import { @@ -277,7 +278,8 @@ export function action< try { const output = await fn(input, { - context: options?.context, + // Context can either be explicitly set, or inherited from the parent action. + context: options?.context ?? getContext(registry), sendChunk: options?.onChunk ?? ((c) => {}), }); diff --git a/js/core/src/auth.ts b/js/core/src/context.ts similarity index 94% rename from js/core/src/auth.ts rename to js/core/src/context.ts index 769865593..50efb988e 100644 --- a/js/core/src/auth.ts +++ b/js/core/src/context.ts @@ -43,16 +43,16 @@ export function runWithContext( * @deprecated use {@link getFlowContext} */ export function getFlowAuth(registry?: Registry | HasRegistry): any { - return getFlowContext(registry); + if (!registry) { + return legacyContextAsyncLocalStorage.getStore(); + } + return getContext(registry); } /** * Gets the runtime context of the current flow. */ -export function getFlowContext(registry?: Registry | HasRegistry): any { - if (!registry) { - return legacyContextAsyncLocalStorage.getStore(); - } +export function getContext(registry: Registry | HasRegistry): any { if ((registry as HasRegistry).registry) { registry = (registry as HasRegistry).registry; } diff --git a/js/core/src/flow.ts b/js/core/src/flow.ts index 7e9c3eccc..72c99c64e 100644 --- a/js/core/src/flow.ts +++ b/js/core/src/flow.ts @@ -27,7 +27,7 @@ import { sentinelNoopStreamingCallback, StreamingCallback, } from './action.js'; -import { runWithContext } from './auth.js'; +import { runWithContext } from './context.js'; import { getErrorMessage, getErrorStack } from './error.js'; import { logger } from './logging.js'; import { HasRegistry, Registry } from './registry.js'; @@ -137,6 +137,25 @@ interface StreamingResponse< output: Promise>; } +/** + * Flow execution context for flow to access the streaming callback and + * side-channel context data. The context itself is a function, a short-cut + * for streaming callback. + */ +export interface FlowSideChannel { + (chunk: S): void; + + /** + * Streaming callback (optional). + */ + sendChunk: StreamingCallback; + + /** + * Additional runtime context data (ex. auth context data). + */ + context?: any; +} + /** * Function to be executed in the flow. */ @@ -148,7 +167,7 @@ export type FlowFn< /** Input to the flow. */ input: z.infer, /** Callback for streaming functions only. */ - streamingCallback: StreamingCallback> + streamingCallback: FlowSideChannel> ) => Promise> | z.infer; export class Flow< @@ -551,9 +570,14 @@ function defineFlowAction< }, async (input, { sendChunk, context }) => { await config.authPolicy?.(context, input); - return await legacyRegistryAls.run(registry, () => - runWithContext(registry, context, () => fn(input, sendChunk)) - ); + return await legacyRegistryAls.run(registry, () => { + const ctx = sendChunk; + (ctx as FlowSideChannel>).sendChunk = sendChunk; + (ctx as FlowSideChannel>).context = context; + return runWithContext(registry, context, () => + fn(input, ctx as FlowSideChannel>) + ); + }); } ); } diff --git a/js/core/src/index.ts b/js/core/src/index.ts index f8d4ce85a..29e06d89f 100644 --- a/js/core/src/index.ts +++ b/js/core/src/index.ts @@ -22,7 +22,7 @@ export const GENKIT_REFLECTION_API_SPEC_VERSION = 1; export { z } from 'zod'; export * from './action.js'; -export { getFlowAuth } from './auth.js'; +export { getFlowAuth } from './context.js'; export { GenkitError } from './error.js'; export { Flow, diff --git a/js/core/tests/flow_test.ts b/js/core/tests/flow_test.ts index cecbab523..a54423747 100644 --- a/js/core/tests/flow_test.ts +++ b/js/core/tests/flow_test.ts @@ -17,7 +17,6 @@ import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; -import { getFlowContext } from '../src/auth.js'; import { defineFlow, defineStreamingFlow, run } from '../src/flow.js'; import { defineAction, getFlowAuth, z } from '../src/index.js'; import { Registry } from '../src/registry.js'; @@ -226,8 +225,8 @@ describe('flow', () => { }); }); - describe('getFlowContext', () => { - it('should run the flow', async () => { + describe('context', () => { + it('should run the flow with context (old way)', async () => { const testFlow = defineFlow( registry, { @@ -236,7 +235,7 @@ describe('flow', () => { outputSchema: z.string(), }, async (input) => { - return `bar ${input} ${JSON.stringify(getFlowContext())}`; + return `bar ${input} ${JSON.stringify(getFlowAuth())}`; } ); @@ -247,7 +246,59 @@ describe('flow', () => { assert.equal(response, 'bar foo {"user":"test-user"}'); }); - it('should streams the flow', async () => { + it('should run the flow with context (new way)', async () => { + const testFlow = defineFlow( + registry, + { + name: 'testFlow', + inputSchema: z.string(), + outputSchema: z.string(), + }, + async (input, { context }) => { + return `bar ${input} ${JSON.stringify(context)}`; + } + ); + + const response = await testFlow('foo', { + context: { user: 'test-user' }, + }); + + assert.equal(response, 'bar foo {"user":"test-user"}'); + }); + + it('should inherit context from the parent', async () => { + const childFlow = defineFlow( + registry, + { + name: 'childFlow', + inputSchema: z.string(), + outputSchema: z.string(), + }, + async (input, { context }) => { + return `bar ${input} ${JSON.stringify(context)}`; + } + ); + + const parentFlow = defineFlow( + registry, + { + name: 'testFlow', + inputSchema: z.string(), + outputSchema: z.string(), + }, + async (input) => { + return childFlow(input); + } + ); + + const response = await parentFlow('foo', { + context: { user: 'test-user' }, + }); + + assert.equal(response, 'bar foo {"user":"test-user"}'); + }); + + it('should streams the flow with context (old way)', async () => { const testFlow = defineStreamingFlow( registry, { @@ -262,7 +313,37 @@ describe('flow', () => { streamingCallback({ count: i }); } } - return `bar ${input} ${!!streamingCallback} ${JSON.stringify(getFlowContext())}`; + return `bar ${input} ${!!streamingCallback} ${JSON.stringify(getFlowAuth())}`; + } + ); + + const response = testFlow(3, { + context: { user: 'test-user' }, + }); + + const gotChunks: any[] = []; + for await (const chunk of response.stream) { + gotChunks.push(chunk); + } + + assert.equal(await response.output, 'bar 3 true {"user":"test-user"}'); + assert.deepEqual(gotChunks, [{ count: 0 }, { count: 1 }, { count: 2 }]); + }); + + it('should streams the flow with context (new way)', async () => { + const testFlow = defineStreamingFlow( + registry, + { + name: 'testFlow', + inputSchema: z.number(), + outputSchema: z.string(), + streamSchema: z.object({ count: z.number() }), + }, + async (input, { sendChunk, context }) => { + for (let i = 0; i < input; i++) { + sendChunk({ count: i }); + } + return `bar ${input} ${!!sendChunk} ${JSON.stringify(context)}`; } ); diff --git a/js/plugins/express/tests/express_test.ts b/js/plugins/express/tests/express_test.ts index d3ebebde0..4f5a8cf98 100644 --- a/js/plugins/express/tests/express_test.ts +++ b/js/plugins/express/tests/express_test.ts @@ -22,7 +22,6 @@ import getPort from 'get-port'; import * as http from 'http'; import assert from 'node:assert'; import { afterEach, beforeEach, describe, it } from 'node:test'; -import { getFlowContext } from '../../../core/lib/auth.js'; import { RequestWithAuth, handler } from '../src/index.js'; describe('telemetry', async () => { @@ -76,8 +75,8 @@ describe('telemetry', async () => { name: 'flowWithContext', inputSchema: z.object({ question: z.string() }), }, - async (input) => { - return `${input.question} - ${JSON.stringify(getFlowContext())}`; + async (input, { context }) => { + return `${input.question} - ${JSON.stringify(context)}`; } ); From a2bf8a32a219c0d1f2a6505ea98c0f50db6d503e Mon Sep 17 00:00:00 2001 From: Michael Doyle Date: Thu, 12 Dec 2024 17:18:50 -0500 Subject: [PATCH 074/562] Add prompt metadata to registry for function-based prompts (#1484) --- js/genkit/src/genkit.ts | 35 ++++++++++++---- js/genkit/tests/prompts_test.ts | 69 +++++++++++++++++++++++++++---- js/plugins/dotprompt/src/index.ts | 2 + 3 files changed, 89 insertions(+), 17 deletions(-) diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index a7c81abc1..0565ea0ff 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -130,6 +130,7 @@ import { PromptMetadata as DotpromptPromptMetadata, loadPromptFolder, prompt, + toFrontmatter, } from '@genkit-ai/dotprompt'; import { v4 as uuidv4 } from 'uuid'; import { BaseEvalDataPointSchema } from './evaluator.js'; @@ -360,7 +361,16 @@ export class Genkit implements HasRegistry { } /** - * Defines and registers a function-based prompt. + * Defines and registers a prompt based on a function. + * + * This is an alternative to defining and importing a .prompt file, providing + * the most advanced control over how the final request to the model is made. + * + * @param options - Prompt metadata including model, model params, + * input/output schemas, etc + * @param fn - A function that returns a {@link GenerateRequest}. Any config + * parameters specified by the {@link GenerateRequest} will take precedence + * over any parameters specified by `options`. * * ```ts * const hi = ai.definePrompt( @@ -394,9 +404,13 @@ export class Genkit implements HasRegistry { ): ExecutablePrompt, O, CustomOptions>; /** - * Defines and registers a dotprompt. + * Defines and registers a prompt based on a template. * - * This is an alternative to defining and importing a .prompt file. + * This is an alternative to defining and importing a .prompt file, in + * situations where a static definition will not suffice. + * + * @param options - The first input number + * @param fn - The second input number * * ```ts * const hi = ai.definePrompt( @@ -427,10 +441,7 @@ export class Genkit implements HasRegistry { O extends z.ZodTypeAny = z.ZodTypeAny, CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, >( - options: PromptMetadata & { - /** The name of the prompt. */ - name: string; - }, + options: PromptMetadata, templateOrFn: string | PromptFn ): ExecutablePrompt, O, CustomOptions> { if (!options.name) { @@ -458,9 +469,17 @@ export class Genkit implements HasRegistry { this.registry, { name: options.name!, + description: options.description, inputJsonSchema: options.input?.jsonSchema, inputSchema: options.input?.schema, - description: options.description, + metadata: { + type: 'prompt', + // TODO: As a stop-gap, we are using the dotprompt interpretation of + // the "prompt metadata", which is roughly the same as the dotprompt + // frontmatter schema. This should be inverted, such that Genkit + // defines the metadata spec and registered dotprompts conform. + prompt: toFrontmatter(options), + }, }, async (input: z.infer) => { const response = await (templateOrFn as PromptFn)(input); diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index d1d58b066..8a2b1e2c5 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -18,7 +18,7 @@ import { modelRef } from '@genkit-ai/ai/model'; import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; import { Genkit, genkit } from '../src/genkit'; -import { z } from '../src/index'; +import { PromptAction, z } from '../src/index'; import { ProgrammableModel, defineEchoModel, @@ -509,7 +509,7 @@ describe('definePrompt', () => { defineEchoModel(ai); }); - it('calls dotprompt with default model', async () => { + it('calls prompt with default model', async () => { const hi = ai.definePrompt( { name: 'hi', @@ -532,7 +532,7 @@ describe('definePrompt', () => { assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); }); - it('calls dotprompt with default model with config', async () => { + it('calls prompt with default model with config', async () => { const hi = ai.definePrompt( { name: 'hi', @@ -561,7 +561,7 @@ describe('definePrompt', () => { ); }); - it('calls dotprompt with default model via retrieved prompt', async () => { + it('calls prompt with default model via retrieved prompt', async () => { ai.definePrompt( { name: 'hi', @@ -599,7 +599,7 @@ describe('definePrompt', () => { defineEchoModel(ai); }); - it('calls dotprompt with default model', async () => { + it('calls prompt with default model', async () => { const hi = ai.definePrompt( { name: 'hi', @@ -622,7 +622,7 @@ describe('definePrompt', () => { assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); }); - it('streams dotprompt with default model', async () => { + it('streams prompt with default model', async () => { const hi = ai.definePrompt( { name: 'hi', @@ -667,7 +667,7 @@ describe('definePrompt', () => { defineEchoModel(ai); }); - it('calls dotprompt with default model', async () => { + it('calls prompt with default model', async () => { const hi = ai.definePrompt( { name: 'hi', @@ -691,7 +691,7 @@ describe('definePrompt', () => { assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); }); - it('calls dotprompt with default model with config', async () => { + it('calls prompt with default model with config', async () => { const hi = ai.definePrompt( { name: 'hi', @@ -721,7 +721,7 @@ describe('definePrompt', () => { ); }); - it('calls dotprompt with default model with call site config', async () => { + it('calls prompt with default model with call site config', async () => { const hi = ai.definePrompt( { name: 'hi', @@ -870,6 +870,57 @@ describe('prompt', () => { assert.strictEqual(text, 'Echo: hi banana; config: {"temperature":11}'); }); + it('includes metadata for functional prompts', async () => { + ai.definePrompt( + { + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), + }, + config: { + temperature: 0.13, + }, + }, + async (input) => { + return { + messages: [], + config: { + temperature: 11, + }, + }; + } + ); + const testPrompt: PromptAction = + await ai.registry.lookupAction('/prompt/hi'); + + assert.deepStrictEqual(testPrompt.__action.metadata, { + type: 'prompt', + prompt: { + name: 'hi', + model: 'echoModel', + config: { + temperature: 0.13, + }, + input: { + schema: { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['name'], + additionalProperties: true, + $schema: 'http://json-schema.org/draft-07/schema#', + }, + }, + }, + }); + }); + it('passes in output options to the model', async () => { const hi = ai.definePrompt( { diff --git a/js/plugins/dotprompt/src/index.ts b/js/plugins/dotprompt/src/index.ts index 1e6d6ffa1..d68d20c64 100644 --- a/js/plugins/dotprompt/src/index.ts +++ b/js/plugins/dotprompt/src/index.ts @@ -17,6 +17,7 @@ import { Registry } from '@genkit-ai/core/registry'; import { readFileSync } from 'fs'; import { basename } from 'path'; +import { toFrontmatter } from './metadata.js'; import { defineDotprompt, Dotprompt, @@ -31,6 +32,7 @@ export { defineDotprompt, Dotprompt, loadPromptFolder, + toFrontmatter, type PromptGenerateOptions, }; From d68ed2f6d59fa5c4b769f301cae0b8c876e7e2b5 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 12 Dec 2024 17:22:42 -0500 Subject: [PATCH 075/562] feat(js/plugins/googleai): adds gemini() function for unspecified model support (#1467) Co-authored-by: Michael Bleigh --- js/plugins/googleai/package.json | 2 +- js/plugins/googleai/src/gemini.ts | 77 +++++++++++-- js/plugins/googleai/src/index.ts | 30 ++++- js/plugins/googleai/tests/gemini_test.ts | 136 ++++++++++++++++++++++- js/pnpm-lock.yaml | 9 ++ js/testapps/flow-simple-ai/package.json | 7 +- 6 files changed, 247 insertions(+), 14 deletions(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 2d8738e11..31513f012 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -21,7 +21,7 @@ "build:clean": "rimraf ./lib", "build": "npm-run-all build:clean check compile", "build:watch": "tsup-node --watch", - "test": "tsx --test ./tests/**/*_test.ts" + "test": "tsx --test ./tests/*_test.ts ./tests/**/*_test.ts" }, "repository": { "type": "git", diff --git a/js/plugins/googleai/src/gemini.ts b/js/plugins/googleai/src/gemini.ts index 67b1932d6..ccc381063 100644 --- a/js/plugins/googleai/src/gemini.ts +++ b/js/plugins/googleai/src/gemini.ts @@ -92,6 +92,7 @@ export const GeminiConfigSchema = GenerationCommonConfigSchema.extend({ }) .optional(), }); +export type GeminiConfig = z.infer; export const gemini10Pro = modelRef({ name: 'googleai/gemini-1.0-pro', @@ -189,13 +190,73 @@ export const SUPPORTED_V15_MODELS = { 'gemini-2.0-flash-exp': gemini20FlashExp, }; -export const SUPPORTED_GEMINI_MODELS: Record< - string, - ModelReference -> = { +export const GENERIC_GEMINI_MODEL = modelRef({ + name: 'googleai/gemini', + configSchema: GeminiConfigSchema, + info: { + label: 'Google Gemini', + supports: { + multiturn: true, + media: true, + tools: true, + systemRole: true, + }, + }, +}); + +export const SUPPORTED_GEMINI_MODELS = { ...SUPPORTED_V1_MODELS, ...SUPPORTED_V15_MODELS, -}; +} as const; + +function longestMatchingPrefix(version: string, potentialMatches: string[]) { + return potentialMatches + .filter((p) => version.startsWith(p)) + .reduce( + (longest, current) => + current.length > longest.length ? current : longest, + '' + ); +} +export type GeminiVersionString = + | keyof typeof SUPPORTED_GEMINI_MODELS + | (string & {}); + +export function gemini( + version: GeminiVersionString, + options: GeminiConfig = {} +): ModelReference { + const nearestModel = nearestGeminiModelRef(version); + return modelRef({ + name: `googleai/${version}`, + config: options, + configSchema: GeminiConfigSchema, + info: { + ...nearestModel.info, + // If exact suffix match for a known model, use its label, otherwise create a new label + label: nearestModel.name.endsWith(version) + ? nearestModel.info?.label + : `Google AI - ${version}`, + }, + }); +} + +function nearestGeminiModelRef( + version: GeminiVersionString, + options: GeminiConfig = {} +): ModelReference { + const matchingKey = longestMatchingPrefix( + version, + Object.keys(SUPPORTED_GEMINI_MODELS) + ); + if (matchingKey) { + return SUPPORTED_GEMINI_MODELS[matchingKey].withConfig({ + ...options, + version, + }); + } + return GENERIC_GEMINI_MODEL.withConfig({ ...options, version }); +} function toGeminiRole( role: MessageData['role'], @@ -501,7 +562,7 @@ export function defineGoogleAIModel( apiVersion?: string, baseUrl?: string, info?: ModelInfo, - defaultConfig?: z.infer + defaultConfig?: GeminiConfig ): ModelAction { if (!apiKey) { apiKey = process.env.GOOGLE_GENAI_API_KEY || process.env.GOOGLE_API_KEY; @@ -519,7 +580,7 @@ export function defineGoogleAIModel( const model: ModelReference = SUPPORTED_GEMINI_MODELS[name] ?? modelRef({ - name, + name: `googleai/${apiModelName}`, info: { label: `Google AI - ${apiModelName}`, supports: { @@ -648,7 +709,7 @@ export function defineGoogleAIModel( let chatRequest: StartChatParams = { systemInstruction, generationConfig, - tools, + tools: tools.length ? tools : undefined, toolConfig, history: messages .slice(0, -1) diff --git a/js/plugins/googleai/src/index.ts b/js/plugins/googleai/src/index.ts index be1de65f1..b9e6ea582 100644 --- a/js/plugins/googleai/src/index.ts +++ b/js/plugins/googleai/src/index.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Genkit } from 'genkit'; +import { Genkit, ModelReference } from 'genkit'; import { GenkitPlugin, genkitPlugin } from 'genkit/plugin'; import { SUPPORTED_MODELS as EMBEDDER_MODELS, @@ -22,9 +22,11 @@ import { textEmbeddingGecko001, } from './embedder.js'; import { + GeminiConfigSchema, SUPPORTED_V15_MODELS, SUPPORTED_V1_MODELS, defineGoogleAIModel, + gemini, gemini10Pro, gemini15Flash, gemini15Flash8b, @@ -32,6 +34,7 @@ import { gemini20FlashExp, } from './gemini.js'; export { + gemini, gemini10Pro, gemini15Flash, gemini15Flash8b, @@ -44,6 +47,7 @@ export interface PluginOptions { apiKey?: string; apiVersion?: string | string[]; baseUrl?: string; + models?: (ModelReference | string)[]; } export function googleAI(options?: PluginOptions): GenkitPlugin { @@ -57,6 +61,7 @@ export function googleAI(options?: PluginOptions): GenkitPlugin { apiVersions = [options?.apiVersion]; } } + if (apiVersions.includes('v1beta')) { Object.keys(SUPPORTED_V15_MODELS).forEach((name) => defineGoogleAIModel( @@ -91,6 +96,29 @@ export function googleAI(options?: PluginOptions): GenkitPlugin { defineGoogleAIEmbedder(ai, name, { apiKey: options?.apiKey }) ); } + + if (options?.models) { + for (const modelOrRef of options?.models) { + const modelName = + typeof modelOrRef === 'string' + ? modelOrRef + : // strip out the `googleai/` prefix + modelOrRef.name.split('/')[1]; + const modelRef = + typeof modelOrRef === 'string' ? gemini(modelOrRef) : modelOrRef; + defineGoogleAIModel( + ai, + modelName, + options?.apiKey, + undefined, + options?.baseUrl, + { + ...modelRef.info, + label: `Google AI - ${modelName}`, + } + ); + } + } }); } diff --git a/js/plugins/googleai/tests/gemini_test.ts b/js/plugins/googleai/tests/gemini_test.ts index 8f5549a81..6ae5da39f 100644 --- a/js/plugins/googleai/tests/gemini_test.ts +++ b/js/plugins/googleai/tests/gemini_test.ts @@ -15,15 +15,21 @@ */ import { GenerateContentCandidate } from '@google/generative-ai'; -import { MessageData } from 'genkit/model'; +import { genkit } from 'genkit'; +import { MessageData, ModelInfo } from 'genkit/model'; import assert from 'node:assert'; -import { describe, it } from 'node:test'; +import { afterEach, beforeEach, describe, it } from 'node:test'; import { + GENERIC_GEMINI_MODEL, cleanSchema, fromGeminiCandidate, + gemini, + gemini15Flash, + gemini15Pro, toGeminiMessage, toGeminiSystemInstruction, } from '../src/gemini.js'; +import { googleAI } from '../src/index.js'; describe('toGeminiMessages', () => { const testCases = [ @@ -378,3 +384,129 @@ describe('cleanSchema', () => { }); }); }); + +describe('plugin', () => { + it('should init the plugin without requiring the api key', async () => { + const ai = genkit({ + plugins: [googleAI()], + }); + + assert.ok(ai); + }); + + describe('plugin', () => { + beforeEach(() => { + process.env.GOOGLE_GENAI_API_KEY = 'testApiKey'; + }); + afterEach(() => { + delete process.env.GOOGLE_GENAI_API_KEY; + }); + + it('should pre-register a few flagship models', async () => { + const ai = genkit({ + plugins: [googleAI()], + }); + + assert.ok(await ai.registry.lookupAction(`/model/${gemini15Flash.name}`)); + assert.ok(await ai.registry.lookupAction(`/model/${gemini15Pro.name}`)); + }); + + it('allow referencing models using `gemini` helper', async () => { + const ai = genkit({ + plugins: [googleAI()], + }); + + const pro = await ai.registry.lookupAction( + `/model/${gemini('gemini-1.5-pro').name}` + ); + assert.ok(pro); + assert.strictEqual(pro.__action.name, 'googleai/gemini-1.5-pro'); + const flash = await ai.registry.lookupAction( + `/model/${gemini('gemini-1.5-flash').name}` + ); + assert.ok(flash); + assert.strictEqual(flash.__action.name, 'googleai/gemini-1.5-flash'); + }); + + it('references explicitly registered models', async () => { + const flash002Ref = gemini('gemini-1.5-flash-002'); + const ai = genkit({ + plugins: [ + googleAI({ + models: ['gemini-1.5-pro-002', flash002Ref, 'gemini-4.0-banana'], + }), + ], + }); + + const pro002Ref = gemini('gemini-1.5-pro-002'); + assert.strictEqual(pro002Ref.name, 'googleai/gemini-1.5-pro-002'); + assertEqualModelInfo( + pro002Ref.info!, + 'Google AI - gemini-1.5-pro-002', + gemini15Pro.info! + ); + const pro002 = await ai.registry.lookupAction(`/model/${pro002Ref.name}`); + assert.ok(pro002); + assert.strictEqual(pro002.__action.name, 'googleai/gemini-1.5-pro-002'); + assertEqualModelInfo( + pro002.__action.metadata?.model, + 'Google AI - gemini-1.5-pro-002', + gemini15Pro.info! + ); + + assert.strictEqual(flash002Ref.name, 'googleai/gemini-1.5-flash-002'); + assertEqualModelInfo( + flash002Ref.info!, + 'Google AI - gemini-1.5-flash-002', + gemini15Flash.info! + ); + const flash002 = await ai.registry.lookupAction( + `/model/${flash002Ref.name}` + ); + assert.ok(flash002); + assert.strictEqual( + flash002.__action.name, + 'googleai/gemini-1.5-flash-002' + ); + assertEqualModelInfo( + flash002.__action.metadata?.model, + 'Google AI - gemini-1.5-flash-002', + gemini15Flash.info! + ); + + const bananaRef = gemini('gemini-4.0-banana'); + assert.strictEqual(bananaRef.name, 'googleai/gemini-4.0-banana'); + assertEqualModelInfo( + bananaRef.info!, + 'Google AI - gemini-4.0-banana', + GENERIC_GEMINI_MODEL.info! // <---- generic model fallback + ); + const banana = await ai.registry.lookupAction(`/model/${bananaRef.name}`); + assert.ok(banana); + assert.strictEqual(banana.__action.name, 'googleai/gemini-4.0-banana'); + assertEqualModelInfo( + banana.__action.metadata?.model, + 'Google AI - gemini-4.0-banana', + GENERIC_GEMINI_MODEL.info! // <---- generic model fallback + ); + + // this one is not registered + const flash003Ref = gemini('gemini-1.5-flash-003'); + assert.strictEqual(flash003Ref.name, 'googleai/gemini-1.5-flash-003'); + const flash003 = await ai.registry.lookupAction( + `/model/${flash003Ref.name}` + ); + assert.ok(flash003 === undefined); + }); + }); +}); + +function assertEqualModelInfo( + modelAction: ModelInfo, + expectedLabel: string, + expectedInfo: ModelInfo +) { + assert.strictEqual(modelAction.label, expectedLabel); + assert.deepStrictEqual(modelAction.supports, expectedInfo.supports); + assert.deepStrictEqual(modelAction.versions, expectedInfo.versions); +} diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 64b2cbe6d..f2adc7290 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -1172,6 +1172,12 @@ importers: '@opentelemetry/sdk-trace-base': specifier: ^1.25.0 version: 1.25.1(@opentelemetry/api@1.9.0) + body-parser: + specifier: ^1.20.3 + version: 1.20.3 + express: + specifier: ^4.21.0 + version: 4.21.0 firebase-admin: specifier: '>=12.2' version: 12.3.1(encoding@0.1.13) @@ -1185,6 +1191,9 @@ importers: rimraf: specifier: ^6.0.1 version: 6.0.1 + tsx: + specifier: ^4.19.2 + version: 4.19.2 typescript: specifier: ^5.3.3 version: 5.4.5 diff --git a/js/testapps/flow-simple-ai/package.json b/js/testapps/flow-simple-ai/package.json index fd0f02e9e..6ca06aad8 100644 --- a/js/testapps/flow-simple-ai/package.json +++ b/js/testapps/flow-simple-ai/package.json @@ -4,7 +4,7 @@ "description": "", "main": "lib/index.js", "scripts": { - "start": "node lib/index.js", + "start": "pnpm exec genkit start -- pnpm exec tsx --watch src/index.ts", "compile": "tsc", "build": "pnpm build:clean && pnpm compile", "build:clean": "rimraf ./lib", @@ -15,18 +15,21 @@ "author": "", "license": "ISC", "dependencies": { - "genkit": "workspace:*", "@genkit-ai/firebase": "workspace:*", "@genkit-ai/google-cloud": "workspace:*", "@genkit-ai/googleai": "workspace:*", "@genkit-ai/vertexai": "workspace:*", "@google/generative-ai": "^0.15.0", "@opentelemetry/sdk-trace-base": "^1.25.0", + "body-parser": "^1.20.3", + "express": "^4.21.0", "firebase-admin": ">=12.2", + "genkit": "workspace:*", "partial-json": "^0.1.7" }, "devDependencies": { "rimraf": "^6.0.1", + "tsx": "^4.19.2", "typescript": "^5.3.3" } } From 0dea56bbb89b2bb73415ff3b5580cb8a2ab508f1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:28:04 +0000 Subject: [PATCH 076/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.0 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 8f727bd6f..6a73f95db 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 828235382a18803e828e1498ed2726840d3ef37d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:28:06 +0000 Subject: [PATCH 077/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.0 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 0a0f7721f..5e67c2a6d 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From 2fefbdd0451fd1352ff654dadb1262473307d132 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:28:08 +0000 Subject: [PATCH 078/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.0 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index 3365fcfaa..2aeadeb38 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From bda11d970f1974639cdad444020dcd2122a92855 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:28:48 +0000 Subject: [PATCH 079/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.0 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 7d57fff78..2cdbf65a0 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From e0fd1c591a1129536b26fefc4ed00db31be01f0a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:28:50 +0000 Subject: [PATCH 080/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.0 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index 9ff081f16..ebf1881be 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From adc16d6c70fddfcfc2aacee261d73b4b5aa2a14a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:28:52 +0000 Subject: [PATCH 081/562] chore: bump genkit version to genkit@1.0.0-rc.0 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index 49d736dca..e77a53f43 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From 873412143caa4add1b7e83309450e54fc7cf29c9 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:29:32 +0000 Subject: [PATCH 082/562] chore: bump genkitx-chromadb version to genkitx-chromadb1.0.0-rc.0 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index d4b18eded..21361edb1 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From f0c21e9c6bf389080f78503b0f4d2a90230fec64 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:29:35 +0000 Subject: [PATCH 083/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.0 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 07eb69aca..3ade10527 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From 950ab333383fc0b3666965be242540094387abbf Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:29:37 +0000 Subject: [PATCH 084/562] chore: bump @genkit-ai/dotprompt version to @genkit-ai/dotprompt@1.0.0-rc.0 --- js/plugins/dotprompt/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dotprompt/package.json b/js/plugins/dotprompt/package.json index 04bca51b7..777aaf00b 100644 --- a/js/plugins/dotprompt/package.json +++ b/js/plugins/dotprompt/package.json @@ -9,7 +9,7 @@ "prompting", "templating" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From 493dfa579ddd942f2d8ae751bbc6f1768c19bbfc Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:29:39 +0000 Subject: [PATCH 085/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.0 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 48af72ef5..3e81073dd 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From 69bc5bed2ca6b15cf12540f4edfc6535a83868c7 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:29:42 +0000 Subject: [PATCH 086/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.0 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 2bd7c5469..53443f42a 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From 0de271b6697d317105175da470c008dfea5e9565 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:29:44 +0000 Subject: [PATCH 087/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.0 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index eb1c5ab37..50ef025ad 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From b886347a41597441cd415eacd3a2fb6219d0493c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:29:47 +0000 Subject: [PATCH 088/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.0 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 31513f012..ed4b72265 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From 0e612dc50c3b24be4e47e588eaf5934e08b2c784 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:29:49 +0000 Subject: [PATCH 089/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.0 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index eb5d2e2fa..f9ce5594c 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From 33d1eee05e2ad6ebf2e848288a4df208f0a6efcb Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:29:52 +0000 Subject: [PATCH 090/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.0 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index 1c00e7d53..fe8af69ef 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From b7b078865136c507d91680684be0ca5c066f01ae Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:29:54 +0000 Subject: [PATCH 091/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.0 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index 53b18cae0..8fdd8681f 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From 699ed4cf9d4bfe6c7a5cd5de7b1f6e7e35e90003 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:29:56 +0000 Subject: [PATCH 092/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.0 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index a1470cee3..8a80e57fa 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From 0e51cd20acbf6b43ca5cadedd49a0c4760f879ad Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:29:59 +0000 Subject: [PATCH 093/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.0 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index 390357261..e54a4df86 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From 9cd083de18ed6e34150d3c7fb3083839d4904402 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 12 Dec 2024 22:30:01 +0000 Subject: [PATCH 094/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.0 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index b821f3b5c..2db8a4dc4 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-dev.0", + "version": "1.0.0-rc.0", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From b8a4618d4b36aaba7b97ab53e413c88a7ae8e57f Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:56:36 +0000 Subject: [PATCH 095/562] fix(genkitEvals): Compatibility issues with Faithfulness / AnswerRelevancy and Vertex JSON mode (#1513) --- .../src/metrics/answer_relevancy.ts | 8 +++---- .../evaluators/src/metrics/faithfulness.ts | 21 +++++++------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/js/plugins/evaluators/src/metrics/answer_relevancy.ts b/js/plugins/evaluators/src/metrics/answer_relevancy.ts index 833a5ce33..0955fde4b 100644 --- a/js/plugins/evaluators/src/metrics/answer_relevancy.ts +++ b/js/plugins/evaluators/src/metrics/answer_relevancy.ts @@ -24,8 +24,8 @@ import { getDirName } from './helper.js'; const AnswerRelevancyResponseSchema = z.object({ question: z.string(), - answered: z.literal(0).or(z.literal(1)), - noncommittal: z.literal(0).or(z.literal(1)), + answered: z.enum(['0', '1'] as const), + noncommittal: z.enum(['0', '1'] as const), }); export async function answerRelevancyScore< @@ -77,8 +77,8 @@ export async function answerRelevancyScore< options: embedderOptions, }); const score = cosineSimilarity(questionEmbed, genQuestionEmbed); - const answered = response.output?.answered === 1; - const isNonCommittal = response.output?.noncommittal === 1; + const answered = response.output?.answered === '1' ? 1 : 0; + const isNonCommittal = response.output?.noncommittal === '1' ? 1 : 0; const answeredPenalty = !answered ? 0.5 : 0; const adjustedScore = score - answeredPenalty < 0 ? 0 : score - answeredPenalty; diff --git a/js/plugins/evaluators/src/metrics/faithfulness.ts b/js/plugins/evaluators/src/metrics/faithfulness.ts index 244d0f10a..b0f8a4204 100644 --- a/js/plugins/evaluators/src/metrics/faithfulness.ts +++ b/js/plugins/evaluators/src/metrics/faithfulness.ts @@ -25,14 +25,11 @@ const LongFormResponseSchema = z.object({ statements: z.array(z.string()) }); const NliResponseBaseSchema = z.object({ statement: z.string(), reason: z.string(), - verdict: z.union([z.literal(0), z.literal(1)]), + verdict: z.enum(['0', '1'] as const), }); type NliResponseBase = z.infer; -const NliResponseSchema = z.union([ - NliResponseBaseSchema, - z.array(NliResponseBaseSchema), -]); +const NliResponseSchema = z.array(NliResponseBaseSchema); /** * @@ -101,19 +98,15 @@ export async function faithfulnessScore< } } -function nliResponseToScore( - input: NliResponseBase[] | NliResponseBase | null -): Score { +function nliResponseToScore(input: NliResponseBase[] | null): Score { if (!input) { throw new Error(`Evaluator response empty`); } - let responses: NliResponseBase[]; - responses = Array.isArray(input) ? input : [input]; - const faithfulStatements = responses.reduce((total, resp) => { - return total + resp.verdict; + const faithfulStatements = input.reduce((total, resp) => { + return total + (resp.verdict === '1' ? 1 : 0); }, 0); return { - score: faithfulStatements / responses.length, - details: { reasoning: responses.map((r) => r.reason).join('; ') }, + score: faithfulStatements / input.length, + details: { reasoning: input.map((r) => r.reason).join('; ') }, }; } From 7893efd2655c0b1251d10afa927dc6edda56f478 Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:57:19 +0000 Subject: [PATCH 096/562] feat(evals): Make context support `any` type. (#1517) --- .../cli/src/commands/eval-extract-data.ts | 6 +- genkit-tools/common/src/eval/evaluate.ts | 7 +- genkit-tools/common/src/plugin/config.ts | 2 +- genkit-tools/common/src/types/eval.ts | 2 +- genkit-tools/common/src/utils/eval.ts | 61 ++++++++------- genkit-tools/common/tests/utils/eval_test.ts | 76 ++++++++----------- .../src/metrics/answer_relevancy.ts | 26 +++++-- .../evaluators/src/metrics/faithfulness.ts | 25 ++++-- .../evaluators/src/metrics/maliciousness.ts | 25 ++++-- .../vertexai/src/evaluation/evaluation.ts | 26 ++++--- 10 files changed, 148 insertions(+), 108 deletions(-) diff --git a/genkit-tools/cli/src/commands/eval-extract-data.ts b/genkit-tools/cli/src/commands/eval-extract-data.ts index e90c6ff55..509eb7fef 100644 --- a/genkit-tools/cli/src/commands/eval-extract-data.ts +++ b/genkit-tools/cli/src/commands/eval-extract-data.ts @@ -74,7 +74,7 @@ export const evalExtractData = new Command('eval:extractData') testCaseId: generateTestCaseId(), input: extractors.input(trace), output: extractors.output(trace), - context: JSON.parse(extractors.context(trace)) as string[], + context: toArray(extractors.context(trace)), // The trace (t) does not contain the traceId, so we have to pull it out of the // spans, de- dupe, and turn it back into an array. traceIds: Array.from( @@ -105,3 +105,7 @@ export const evalExtractData = new Command('eval:extractData') } }); }); + +function toArray(input: any) { + return Array.isArray(input) ? input : [input]; +} diff --git a/genkit-tools/common/src/eval/evaluate.ts b/genkit-tools/common/src/eval/evaluate.ts index 09f01b913..5cdaa0faa 100644 --- a/genkit-tools/common/src/eval/evaluate.ts +++ b/genkit-tools/common/src/eval/evaluate.ts @@ -376,7 +376,7 @@ async function gatherEvalInput(params: { input, output, error, - context: JSON.parse(context) as string[], + context: Array.isArray(context) ? context : [context], reference: state.reference, traceIds: [traceId], }; @@ -395,12 +395,11 @@ function getSpanErrorMessage(span: SpanData): string | undefined { } } -function getErrorFromModelResponse(output: string): string | undefined { - const obj = JSON.parse(output); +function getErrorFromModelResponse(obj: any): string | undefined { const response = GenerateResponseSchema.parse(obj); if (!response || !response.candidates || response.candidates.length === 0) { - return `No response was extracted from the output. '${output}'`; + return `No response was extracted from the output. '${JSON.stringify(obj)}'`; } // We currently only support the first candidate diff --git a/genkit-tools/common/src/plugin/config.ts b/genkit-tools/common/src/plugin/config.ts index 608e1380a..3b9e576b3 100644 --- a/genkit-tools/common/src/plugin/config.ts +++ b/genkit-tools/common/src/plugin/config.ts @@ -44,7 +44,7 @@ const EvaluationExtractorSchema = z.record( z.union([ z.string(), // specify the displayName (default to output) StepSelectorSchema, //, {inputOf: 'my-step-name'} - z.function().args(TraceDataSchema).returns(z.string()), // custom trace extractor + z.function().args(TraceDataSchema).returns(z.any()), // custom trace extractor ]) ); export type EvaluationExtractor = z.infer; diff --git a/genkit-tools/common/src/types/eval.ts b/genkit-tools/common/src/types/eval.ts index b83b58a43..f9c1ed867 100644 --- a/genkit-tools/common/src/types/eval.ts +++ b/genkit-tools/common/src/types/eval.ts @@ -81,7 +81,7 @@ export const EvalInputSchema = z.object({ input: z.any(), output: z.any(), error: z.string().optional(), - context: z.array(z.string()).optional(), + context: z.array(z.any()).optional(), reference: z.any().optional(), traceIds: z.array(z.string()), }); diff --git a/genkit-tools/common/src/utils/eval.ts b/genkit-tools/common/src/utils/eval.ts index 3bfdc0fc8..87204db04 100644 --- a/genkit-tools/common/src/utils/eval.ts +++ b/genkit-tools/common/src/utils/eval.ts @@ -31,8 +31,7 @@ import { NestedSpanData, TraceData } from '../types/trace'; import { logger } from './logger'; import { stackTraceSpans } from './trace'; -export type EvalExtractorFn = (t: TraceData) => string; -const JSON_EMPTY_STRING = '""'; +export type EvalExtractorFn = (t: TraceData) => any; export const EVALUATOR_ACTION_PREFIX = '/evaluator'; @@ -78,30 +77,39 @@ function getRootSpan(trace: TraceData): NestedSpanData | undefined { return stackTraceSpans(trace); } +function safeParse(value?: string) { + if (value) { + try { + return JSON.parse(value); + } catch (e) { + return ''; + } + } + return ''; +} + const DEFAULT_INPUT_EXTRACTOR: EvalExtractorFn = (trace: TraceData) => { const rootSpan = getRootSpan(trace); - return (rootSpan?.attributes['genkit:input'] as string) || JSON_EMPTY_STRING; + return safeParse(rootSpan?.attributes['genkit:input'] as string); }; const DEFAULT_OUTPUT_EXTRACTOR: EvalExtractorFn = (trace: TraceData) => { const rootSpan = getRootSpan(trace); - return (rootSpan?.attributes['genkit:output'] as string) || JSON_EMPTY_STRING; + return safeParse(rootSpan?.attributes['genkit:output'] as string); }; const DEFAULT_CONTEXT_EXTRACTOR: EvalExtractorFn = (trace: TraceData) => { - return JSON.stringify( - Object.values(trace.spans) - .filter((s) => s.attributes['genkit:metadata:subtype'] === 'retriever') - .flatMap((s) => { - const output: RetrieverResponse = JSON.parse( - s.attributes['genkit:output'] as string - ); - if (!output) { - return []; - } - return output.documents.flatMap((d: DocumentData) => - d.content.map((c) => c.text).filter((text): text is string => !!text) - ); - }) - ); + return Object.values(trace.spans) + .filter((s) => s.attributes['genkit:metadata:subtype'] === 'retriever') + .flatMap((s) => { + const output: RetrieverResponse = safeParse( + s.attributes['genkit:output'] as string + ); + if (!output) { + return []; + } + return output.documents.flatMap((d: DocumentData) => + d.content.map((c) => c.text).filter((text): text is string => !!text) + ); + }); }; const DEFAULT_FLOW_EXTRACTORS: Record = { @@ -113,29 +121,29 @@ const DEFAULT_FLOW_EXTRACTORS: Record = { const DEFAULT_MODEL_EXTRACTORS: Record = { input: DEFAULT_INPUT_EXTRACTOR, output: DEFAULT_OUTPUT_EXTRACTOR, - context: () => JSON.stringify([]), + context: () => [], }; function getStepAttribute( trace: TraceData, stepName: string, attributeName?: string -): string { +) { // Default to output const attr = attributeName ?? 'genkit:output'; const values = Object.values(trace.spans) .filter((step) => step.displayName === stepName) .flatMap((step) => { - return JSON.parse(step.attributes[attr] as string); + return safeParse(step.attributes[attr] as string); }); if (values.length === 0) { - return JSON_EMPTY_STRING; + return ''; } if (values.length === 1) { - return JSON.stringify(values[0]); + return values[0]; } // Return array if multiple steps have the same name - return JSON.stringify(values); + return values; } function getExtractorFromStepName(stepName: string): EvalExtractorFn { @@ -159,7 +167,7 @@ function getExtractorFromStepSelector( selectedAttribute = 'genkit:output'; } if (!stepName) { - return JSON_EMPTY_STRING; + return ''; } else { return getStepAttribute(trace, stepName, selectedAttribute); } @@ -196,7 +204,6 @@ export async function getEvalExtractors( return Promise.resolve(DEFAULT_MODEL_EXTRACTORS); } const config = await findToolsConfig(); - logger.info(`Found tools config... ${JSON.stringify(config)}`); const extractors = config?.evaluators ?.filter((e) => e.actionRef === actionRef) .map((e) => e.extractors); diff --git a/genkit-tools/common/tests/utils/eval_test.ts b/genkit-tools/common/tests/utils/eval_test.ts index e28948515..ade2e3a0b 100644 --- a/genkit-tools/common/tests/utils/eval_test.ts +++ b/genkit-tools/common/tests/utils/eval_test.ts @@ -38,9 +38,9 @@ describe('eval utils', () => { expect(Object.keys(extractors).sort()).toEqual( ['input', 'output', 'context'].sort() ); - expect(extractors.input(trace)).toEqual(JSON.stringify('My input')); - expect(extractors.output(trace)).toEqual(JSON.stringify('My output')); - expect(extractors.context(trace)).toEqual(JSON.stringify([])); + expect(extractors.input(trace)).toEqual('My input'); + expect(extractors.output(trace)).toEqual('My output'); + expect(extractors.context(trace)).toEqual([]); }); }); @@ -63,9 +63,9 @@ describe('eval utils', () => { expect(Object.keys(extractors).sort()).toEqual( ['input', 'output', 'context'].sort() ); - expect(extractors.input(trace)).toEqual(JSON.stringify('My input')); - expect(extractors.output(trace)).toEqual(JSON.stringify('My output')); - expect(extractors.context(trace)).toEqual(JSON.stringify(CONTEXT_TEXTS)); + expect(extractors.input(trace)).toEqual('My input'); + expect(extractors.output(trace)).toEqual('My output'); + expect(extractors.context(trace)).toEqual(CONTEXT_TEXTS); }); it('returns custom extractors by stepName', async () => { @@ -100,11 +100,9 @@ describe('eval utils', () => { const extractors = await getEvalExtractors('/flow/multiSteps'); - expect(extractors.input(trace)).toEqual(JSON.stringify('My input')); - expect(extractors.output(trace)).toEqual( - JSON.stringify({ out: 'my-object-output' }) - ); - expect(extractors.context(trace)).toEqual(JSON.stringify(CONTEXT_TEXTS)); + expect(extractors.input(trace)).toEqual('My input'); + expect(extractors.output(trace)).toEqual({ out: 'my-object-output' }); + expect(extractors.context(trace)).toEqual(CONTEXT_TEXTS); }); it('returns custom extractors by stepSelector', async () => { @@ -146,11 +144,9 @@ describe('eval utils', () => { const extractors = await getEvalExtractors('/flow/multiSteps'); - expect(extractors.input(trace)).toEqual(JSON.stringify('My input')); - expect(extractors.output(trace)).toEqual(JSON.stringify('step2-input')); - expect(extractors.context(trace)).toEqual( - JSON.stringify(['Hello', 'World']) - ); + expect(extractors.input(trace)).toEqual('My input'); + expect(extractors.output(trace)).toEqual('step2-input'); + expect(extractors.context(trace)).toEqual(['Hello', 'World']); }); it('returns custom extractors by trace function', async () => { @@ -160,23 +156,21 @@ describe('eval utils', () => { actionRef: '/flow/multiSteps', extractors: { input: (trace: TraceData) => { - return JSON.stringify( - Object.values(trace.spans) - .filter( - (s) => - s.attributes['genkit:type'] === 'action' && - s.attributes['genkit:metadata:subtype'] !== 'retriever' - ) - .map((s) => { - const inputValue = JSON.parse( - s.attributes['genkit:input'] as string - ).start.input; - if (!inputValue) { - return ''; - } - return inputValue + ' TEST TEST TEST'; - }) - ); + return Object.values(trace.spans) + .filter( + (s) => + s.attributes['genkit:type'] === 'action' && + s.attributes['genkit:metadata:subtype'] !== 'retriever' + ) + .map((s) => { + const inputValue = JSON.parse( + s.attributes['genkit:input'] as string + ).start.input; + if (!inputValue) { + return ''; + } + return inputValue + ' TEST TEST TEST'; + }); }, output: { inputOf: 'step2' }, context: { outputOf: 'step3-array' }, @@ -211,13 +205,9 @@ describe('eval utils', () => { const extractors = await getEvalExtractors('/flow/multiSteps'); - expect(extractors.input(trace)).toEqual( - JSON.stringify(['My input TEST TEST TEST']) - ); - expect(extractors.output(trace)).toEqual(JSON.stringify('step2-input')); - expect(extractors.context(trace)).toEqual( - JSON.stringify(['Hello', 'World']) - ); + expect(extractors.input(trace)).toEqual(['My input TEST TEST TEST']); + expect(extractors.output(trace)).toEqual('step2-input'); + expect(extractors.context(trace)).toEqual(['Hello', 'World']); }); it('returns runs default extractors when trace fails', async () => { @@ -239,8 +229,8 @@ describe('eval utils', () => { expect(Object.keys(extractors).sort()).toEqual( ['input', 'output', 'context'].sort() ); - expect(extractors.input(trace)).toEqual(JSON.stringify('My input')); - expect(extractors.output(trace)).toEqual(JSON.stringify('')); - expect(extractors.context(trace)).toEqual(JSON.stringify(CONTEXT_TEXTS)); + expect(extractors.input(trace)).toEqual('My input'); + expect(extractors.output(trace)).toEqual(''); + expect(extractors.context(trace)).toEqual(CONTEXT_TEXTS); }); }); diff --git a/js/plugins/evaluators/src/metrics/answer_relevancy.ts b/js/plugins/evaluators/src/metrics/answer_relevancy.ts index 0955fde4b..e8d101d6f 100644 --- a/js/plugins/evaluators/src/metrics/answer_relevancy.ts +++ b/js/plugins/evaluators/src/metrics/answer_relevancy.ts @@ -40,12 +40,26 @@ export async function answerRelevancyScore< embedderOptions?: z.infer ): Promise { try { - if (!dataPoint.context?.length) { - throw new Error('Context was not provided'); + if (!dataPoint.input) { + throw new Error('Input was not provided'); } if (!dataPoint.output) { throw new Error('Output was not provided'); } + if (!dataPoint.context?.length) { + throw new Error('Context was not provided'); + } + + const input = + typeof dataPoint.input === 'string' + ? dataPoint.input + : JSON.stringify(dataPoint.input); + const output = + typeof dataPoint.output === 'string' + ? dataPoint.output + : JSON.stringify(dataPoint.output); + const context = dataPoint.context.map((i) => JSON.stringify(i)); + const prompt = await loadPromptFile( ai.registry, path.resolve(getDirName(), '../../prompts/answer_relevancy.prompt') @@ -54,9 +68,9 @@ export async function answerRelevancyScore< model: judgeLlm, config: judgeConfig, prompt: prompt.renderText({ - question: dataPoint.input as string, - answer: dataPoint.output as string, - context: dataPoint.context.join(' '), + question: input, + answer: output, + context: context.join(' '), }), output: { schema: AnswerRelevancyResponseSchema, @@ -68,7 +82,7 @@ export async function answerRelevancyScore< const questionEmbed = await ai.embed({ embedder, - content: dataPoint.input as string, + content: input, options: embedderOptions, }); const genQuestionEmbed = await ai.embed({ diff --git a/js/plugins/evaluators/src/metrics/faithfulness.ts b/js/plugins/evaluators/src/metrics/faithfulness.ts index b0f8a4204..3ee97664c 100644 --- a/js/plugins/evaluators/src/metrics/faithfulness.ts +++ b/js/plugins/evaluators/src/metrics/faithfulness.ts @@ -43,13 +43,26 @@ export async function faithfulnessScore< judgeConfig?: CustomModelOptions ): Promise { try { - const { input, output, context } = dataPoint; - if (!context?.length) { - throw new Error('Context was not provided'); + if (!dataPoint.input) { + throw new Error('Input was not provided'); } - if (!output) { + if (!dataPoint.output) { throw new Error('Output was not provided'); } + if (!dataPoint.context?.length) { + throw new Error('Context was not provided'); + } + + const input = + typeof dataPoint.input === 'string' + ? dataPoint.input + : JSON.stringify(dataPoint.input); + const output = + typeof dataPoint.output === 'string' + ? dataPoint.output + : JSON.stringify(dataPoint.output); + const context = dataPoint.context.map((i) => JSON.stringify(i)); + const longFormPrompt = await loadPromptFile( ai.registry, path.resolve(getDirName(), '../../prompts/faithfulness_long_form.prompt') @@ -58,8 +71,8 @@ export async function faithfulnessScore< model: judgeLlm, config: judgeConfig, prompt: longFormPrompt.renderText({ - question: input as string, - answer: output as string, + question: input, + answer: output, }), output: { schema: LongFormResponseSchema, diff --git a/js/plugins/evaluators/src/metrics/maliciousness.ts b/js/plugins/evaluators/src/metrics/maliciousness.ts index 5538cbc25..92716a2fd 100644 --- a/js/plugins/evaluators/src/metrics/maliciousness.ts +++ b/js/plugins/evaluators/src/metrics/maliciousness.ts @@ -32,11 +32,22 @@ export async function maliciousnessScore< dataPoint: BaseEvalDataPoint, judgeConfig?: CustomModelOptions ): Promise { - const d = dataPoint; try { - if (!d.input || !d.output) { - throw new Error('input and output are required'); + if (!dataPoint.input) { + throw new Error('Input was not provided'); } + if (!dataPoint.output) { + throw new Error('Output was not provided'); + } + + const input = + typeof dataPoint.input === 'string' + ? dataPoint.input + : JSON.stringify(dataPoint.input); + const output = + typeof dataPoint.output === 'string' + ? dataPoint.output + : JSON.stringify(dataPoint.output); const prompt = await loadPromptFile( ai.registry, @@ -47,8 +58,8 @@ export async function maliciousnessScore< model: judgeLlm, config: judgeConfig, prompt: prompt.renderText({ - input: d.input as string, - submission: d.output as string, + input: input, + submission: output, }), output: { schema: MaliciousnessResponseSchema, @@ -64,9 +75,7 @@ export async function maliciousnessScore< }; } catch (err) { console.debug( - `Genkit answer relevancy evaluation failed with error ${err} for sample ${JSON.stringify( - d - )}` + `Genkit answer relevancy evaluation failed with error ${err} for sample ${JSON.stringify(dataPoint)}` ); throw err; } diff --git a/js/plugins/vertexai/src/evaluation/evaluation.ts b/js/plugins/vertexai/src/evaluation/evaluation.ts index 965b9fc86..9d1ef8e9d 100644 --- a/js/plugins/vertexai/src/evaluation/evaluation.ts +++ b/js/plugins/vertexai/src/evaluation/evaluation.ts @@ -49,6 +49,10 @@ export type VertexAIEvaluationMetric = | VertexAIEvaluationMetricType | VertexAIEvaluationMetricConfig; +function stringify(input: unknown) { + return typeof input === 'string' ? input : JSON.stringify(input); +} + export function vertexEvaluators( ai: Genkit, auth: GoogleAuth, @@ -123,7 +127,7 @@ function createBleuEvaluator( metricSpec, instances: [ { - prediction: datapoint.output as string, + prediction: stringify(datapoint.output), reference: datapoint.reference, }, ], @@ -164,7 +168,7 @@ function createRougeEvaluator( rougeInput: { metricSpec, instances: { - prediction: datapoint.output as string, + prediction: stringify(datapoint.output), reference: datapoint.reference, }, }, @@ -204,7 +208,7 @@ function createFluencyEvaluator( fluencyInput: { metricSpec, instance: { - prediction: datapoint.output as string, + prediction: stringify(datapoint.output), }, }, }; @@ -246,7 +250,7 @@ function createSafetyEvaluator( safetyInput: { metricSpec, instance: { - prediction: datapoint.output as string, + prediction: stringify(datapoint.output), }, }, }; @@ -289,7 +293,7 @@ function createGroundednessEvaluator( groundednessInput: { metricSpec, instance: { - prediction: datapoint.output as string, + prediction: stringify(datapoint.output), context: datapoint.context?.join('. '), }, }, @@ -332,8 +336,8 @@ function createSummarizationQualityEvaluator( summarizationQualityInput: { metricSpec, instance: { - prediction: datapoint.output as string, - instruction: datapoint.input as string, + prediction: stringify(datapoint.output), + instruction: stringify(datapoint.input), context: datapoint.context?.join('. '), }, }, @@ -377,8 +381,8 @@ function createSummarizationHelpfulnessEvaluator( summarizationHelpfulnessInput: { metricSpec, instance: { - prediction: datapoint.output as string, - instruction: datapoint.input as string, + prediction: stringify(datapoint.output), + instruction: stringify(datapoint.input), context: datapoint.context?.join('. '), }, }, @@ -421,8 +425,8 @@ function createSummarizationVerbosityEvaluator( summarizationVerbosityInput: { metricSpec, instance: { - prediction: datapoint.output as string, - instruction: datapoint.input as string, + prediction: stringify(datapoint.output), + instruction: stringify(datapoint.input), context: datapoint.context?.join('. '), }, }, From 943736eea39ec167d1a05d72c120e6f220194b5d Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Fri, 13 Dec 2024 18:21:31 +0000 Subject: [PATCH 097/562] let datasetIds start with capitals (#1519) --- .../common/src/eval/localFileDatasetStore.ts | 4 +++- .../tests/eval/localFileDatasetStore_test.ts | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/genkit-tools/common/src/eval/localFileDatasetStore.ts b/genkit-tools/common/src/eval/localFileDatasetStore.ts index 4263b369d..b21408f59 100644 --- a/genkit-tools/common/src/eval/localFileDatasetStore.ts +++ b/genkit-tools/common/src/eval/localFileDatasetStore.ts @@ -203,7 +203,9 @@ export class LocalFileDatasetStore implements DatasetStore { const metadataMap = await this.getMetadataMap(); const keys = Object.keys(metadataMap); if (datasetId) { - const isValid = /^[a-z][A-Za-z0-9_.-]{4,34}[A-Za-z0-9]$/g.test(datasetId); + const isValid = /^[A-Za-z][A-Za-z0-9_.-]{4,34}[A-Za-z0-9]$/g.test( + datasetId + ); if (!isValid) { throw new Error( 'Invalid datasetId provided. ID must be alphanumeric, with hyphens, dots and dashes. Is must start with an alphabet, end with an alphabet or a number, and must be 6-36 characters long.' diff --git a/genkit-tools/common/tests/eval/localFileDatasetStore_test.ts b/genkit-tools/common/tests/eval/localFileDatasetStore_test.ts index 17e811611..cd37cd03f 100644 --- a/genkit-tools/common/tests/eval/localFileDatasetStore_test.ts +++ b/genkit-tools/common/tests/eval/localFileDatasetStore_test.ts @@ -283,6 +283,23 @@ describe('localFileDatasetStore', () => { }); }).rejects.toThrow('Invalid datasetId'); }); + + it('does not fail if datasetId starts with capitals', async () => { + fs.promises.writeFile = jest.fn(async () => Promise.resolve(undefined)); + fs.promises.appendFile = jest.fn(async () => Promise.resolve(undefined)); + // For index file reads + fs.promises.readFile = jest.fn(async () => + Promise.resolve(JSON.stringify({}) as any) + ); + fs.existsSync = jest.fn(() => false); + + expect(async () => { + await DatasetStore.createDataset({ + ...CREATE_DATASET_REQUEST, + datasetId: 'UltracoolId', + }); + }).not.toThrow(); + }); }); describe('updateDataset', () => { From d760135f56fa37599d75e93937025c35883cd90e Mon Sep 17 00:00:00 2001 From: thedmail Date: Fri, 13 Dec 2024 10:36:20 -0800 Subject: [PATCH 098/562] docs: Prepends import statement to code snippet imported via includecode widget. (#1486) --- docs/flows.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/flows.md b/docs/flows.md index b6e13a05a..6bc982eea 100644 --- a/docs/flows.md +++ b/docs/flows.md @@ -7,11 +7,11 @@ must accompany the model call. For example: * Retrieving contextual information to send with the model call * Retrieving the history of the user's current session, for example in a chat - app + app * Using one model to reformat the user input in a way that's suitable to pass - to another model -* Evaluating the "safety" of a model's output before presenting it to the user -* Combining the output of several models + to another model +* Evaluating the "safety" of a model's output before presenting it to the user +* Combining the output of several models Every step of this workflow must work together for any AI-related task to succeed. @@ -22,7 +22,7 @@ they add additional capabilities intended to ease the development of AI features: * **Type safety**: Input and output schemas defined using Zod, which provides - both static and runtime type checking + both static and runtime type checking * **Integration with developer UI**: Debug flows independently of your application code using the developer UI. In the developer UI, you can run flows and view traces for each step of the flow. @@ -37,7 +37,7 @@ flow doesn't need to be flow-aware. ## Defining and calling flows In its simplest form, a flow just wraps a function. The following example wraps -a function that calls `generate()`: +a function that calls `generate()`: ```ts {% includecode github_path="firebase/genkit/js/doc-snippets/src/flows/index.ts" region_tag="ex01" adjust_indentation="auto" %} @@ -48,7 +48,7 @@ doing so lets you run the flow from the Genkit CLI and from the developer UI, and is a requirement for several of Genkit's features, including deployment and observability (later sections discuss these topics). -### Input and output schemas +### Input and output schemas One of the most important advantages Genkit flows have over directly calling a model API is type safety of both inputs and outputs. When defining flows, you @@ -81,7 +81,7 @@ Once you've defined a flow, you can call it from your Node.js code: {% includecode github_path="firebase/genkit/js/doc-snippets/src/flows/index.ts" region_tag="ex04" adjust_indentation="auto" %} ``` -The argument to the flow must conform to the input schema, if you defined one. +The argument to the flow must conform to the input schema, if you defined one. If you defined an output schema, the flow response will conform to it. For example, if you set the output schema to `MenuItemSchema`, the flow output will @@ -91,7 +91,7 @@ contain its properties: {% includecode github_path="firebase/genkit/js/doc-snippets/src/flows/index.ts" region_tag="ex05" adjust_indentation="auto" %} ``` -## Streaming flows +## Streaming flows Flows support streaming using an interface similar to `generate()`'s streaming interface. Streaming is useful when your flow generates a large amount of @@ -148,7 +148,7 @@ Note that the streaming output of a flow might not be the same type as the complete output; the streaming output conforms to `streamSchema`, whereas the complete output conforms to `outputSchema`. -## Running flows from the command line +## Running flows from the command line You can run flows from the command line using the Genkit CLI tool: @@ -167,7 +167,7 @@ Running a flow from the command line is useful for testing a flow, or for running flows that perform tasks needed on an ad hoc basis—for example, to run a flow that ingests a document into your vector database. -## Debugging flows +## Debugging flows One of the advantages of encapsulating AI logic within a flow is that you can test and debug the flow independently from your app using the Genkit developer @@ -224,6 +224,7 @@ some unspecified method, and the second step includes the menu as context for a `generate()` call. ```ts +import { run } from 'genkit'; {% includecode github_path="firebase/genkit/js/doc-snippets/src/flows/index.ts" region_tag="ex11" adjust_indentation="auto" %} ``` From 4e59569d2d9909f4ba676920936a142fb5e99a40 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Fri, 13 Dec 2024 15:09:12 -0500 Subject: [PATCH 099/562] fix(js/plugins/googleai): work around googleai forced tool calling infinite loop (#1522) Co-authored-by: Michael Bleigh --- js/plugins/googleai/src/gemini.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/js/plugins/googleai/src/gemini.ts b/js/plugins/googleai/src/gemini.ts index ccc381063..ede6dcb95 100644 --- a/js/plugins/googleai/src/gemini.ts +++ b/js/plugins/googleai/src/gemini.ts @@ -666,7 +666,12 @@ export function defineGoogleAIModel( } let toolConfig: ToolConfig | undefined; - if (requestConfig.functionCallingConfig) { + if ( + requestConfig.functionCallingConfig && + // This is a workround for issue: https://github.com/firebase/genkit/issues/1520 + // TODO: remove this when the issue is resolved upstream in the Gemini API + !messages.at(-1)?.content.find((c) => c.toolResponse) + ) { toolConfig = { functionCallingConfig: { allowedFunctionNames: From 47c5cd9e2a45a7d4ecfd889c89811742d0598679 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Fri, 13 Dec 2024 17:00:31 -0500 Subject: [PATCH 100/562] feat(js/ai): added `maxTurns` option to generate options (#1516) --- js/ai/src/generate.ts | 3 + js/ai/src/generate/action.ts | 29 ++++- js/genkit/tests/generate_test.ts | 143 +++++++++++++++++++++- js/plugins/dotprompt/src/prompt.ts | 1 + js/plugins/dotprompt/tests/prompt_test.ts | 4 + 5 files changed, 175 insertions(+), 5 deletions(-) diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index 3d686d7fe..a6ad66b94 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -77,6 +77,8 @@ export interface GenerateOptions< output?: OutputOptions; /** When true, return tool calls for manual processing instead of automatically resolving them. */ returnToolRequests?: boolean; + /** Maximum number of tool call iterations that can be performed in a single generate call (default 5). */ + maxTurns?: number; /** When provided, models supporting streaming will call the provided callback with chunks as generation progresses. */ onChunk?: StreamingCallback; /** @@ -282,6 +284,7 @@ export async function generate< jsonSchema: resolvedSchema, }, returnToolRequests: resolvedOptions.returnToolRequests, + maxTurns: resolvedOptions.maxTurns, }; return await runWithStreamingCallback( diff --git a/js/ai/src/generate/action.ts b/js/ai/src/generate/action.ts index bea20b728..94bf6fdd1 100644 --- a/js/ai/src/generate/action.ts +++ b/js/ai/src/generate/action.ts @@ -31,6 +31,7 @@ import { Formatter } from '../formats/types.js'; import { GenerateResponse, GenerateResponseChunk, + GenerationResponseError, tagAsPreamble, } from '../generate.js'; import { @@ -71,6 +72,8 @@ export const GenerateUtilParamSchema = z.object({ .optional(), /** When true, return tool calls for manual processing instead of automatically resolving them. */ returnToolRequests: z.boolean().optional(), + /** Maximum number of tool call iterations that can be performed in a single generate call (default 5). */ + maxTurns: z.number().optional(), }); /** @@ -79,8 +82,10 @@ export const GenerateUtilParamSchema = z.object({ export async function generateHelper( registry: Registry, input: z.infer, - middleware?: ModelMiddleware[] + middleware?: ModelMiddleware[], + currentTurns?: number ): Promise { + currentTurns = currentTurns ?? 0; // do tracing return await runInNewSpan( registry, @@ -95,7 +100,7 @@ export async function generateHelper( async (metadata) => { metadata.name = 'generate'; metadata.input = input; - const output = await generate(registry, input, middleware); + const output = await generate(registry, input, middleware, currentTurns!); metadata.output = JSON.stringify(output); return output; } @@ -105,7 +110,8 @@ export async function generateHelper( async function generate( registry: Registry, rawRequest: z.infer, - middleware?: ModelMiddleware[] + middleware: ModelMiddleware[] | undefined, + currentTurn: number ): Promise { const { modelAction: model } = await resolveModel(registry, rawRequest.model); if (model.__action.metadata?.model.stage === 'deprecated') { @@ -191,6 +197,16 @@ async function generate( if (rawRequest.returnToolRequests || toolCalls.length === 0) { return response.toJSON(); } + const maxIterations = rawRequest.maxTurns ?? 5; + if (currentTurn + 1 > maxIterations) { + throw new GenerationResponseError( + response, + `Exceeded maximum tool call iterations (${maxIterations})`, + 'ABORTED', + { request } + ); + } + const toolResponses: ToolResponsePart[] = []; let messages: MessageData[] = [...request.messages, message]; let newTools = rawRequest.tools; @@ -240,7 +256,12 @@ async function generate( ] as MessageData[], tools: newTools, }; - return await generateHelper(registry, nextRequest, middleware); + return await generateHelper( + registry, + nextRequest, + middleware, + currentTurn + 1 + ); } async function actionToGenerateRequest( diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index 4db291519..028ed2a38 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -18,7 +18,12 @@ import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; import { modelRef } from '../../ai/src/model'; import { Genkit, genkit } from '../src/genkit'; -import { defineEchoModel, runAsync } from './helpers'; +import { + ProgrammableModel, + defineEchoModel, + defineProgrammableModel, + runAsync, +} from './helpers'; describe('generate', () => { describe('default model', () => { @@ -256,4 +261,140 @@ describe('generate', () => { ); }); }); + + describe('tools', () => { + let ai: Genkit; + let pm: ProgrammableModel; + + beforeEach(() => { + ai = genkit({ + model: 'programmableModel', + }); + pm = defineProgrammableModel(ai); + defineEchoModel(ai); + }); + + it('call the tool', async () => { + ai.defineTool( + { name: 'testTool', description: 'description' }, + async () => 'tool called' + ); + + // first response be tools call, the subsequent just text response from agent b. + let reqCounter = 0; + pm.handleResponse = async (req, sc) => { + return { + message: { + role: 'model', + content: [ + reqCounter++ === 0 + ? { + toolRequest: { + name: 'testTool', + input: {}, + ref: 'ref123', + }, + } + : { text: 'done' }, + ], + }, + }; + }; + + const { text } = await ai.generate({ + prompt: 'call the tool', + tools: ['testTool'], + }); + + assert.strictEqual(text, 'done'); + assert.deepStrictEqual( + pm.lastRequest, + + { + config: {}, + messages: [ + { + role: 'user', + content: [{ text: 'call the tool' }], + }, + { + role: 'model', + content: [ + { + toolRequest: { + input: {}, + name: 'testTool', + ref: 'ref123', + }, + }, + ], + }, + { + role: 'tool', + content: [ + { + toolResponse: { + name: 'testTool', + output: 'tool called', + ref: 'ref123', + }, + }, + ], + }, + ], + output: {}, + tools: [ + { + description: 'description', + inputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + name: 'testTool', + outputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + }, + ], + } + ); + }); + + it('throws when exceeding max tool call iterations', async () => { + ai.defineTool( + { name: 'testTool', description: 'description' }, + async () => 'tool called' + ); + + // this will result in the tool getting called infinitely in a loop. + pm.handleResponse = async () => { + return { + message: { + role: 'model', + content: [ + { + toolRequest: { + name: 'testTool', + input: {}, + ref: 'ref123', + }, + }, + ], + }, + }; + }; + + await assert.rejects( + ai.generate({ + prompt: 'call the tool', + tools: ['testTool'], + maxTurns: 17, + }), + (err: Error) => { + return err.message.includes( + 'Exceeded maximum tool call iterations (17)' + ); + } + ); + }); + }); }); diff --git a/js/plugins/dotprompt/src/prompt.ts b/js/plugins/dotprompt/src/prompt.ts index 0ecd9a4c6..52dbdfcff 100644 --- a/js/plugins/dotprompt/src/prompt.ts +++ b/js/plugins/dotprompt/src/prompt.ts @@ -253,6 +253,7 @@ export class Dotprompt implements PromptMetadata { tools: (options.tools || []).concat(this.tools || []), onChunk: options.onChunk ?? options.streamingCallback, returnToolRequests: options.returnToolRequests, + maxTurns: options.maxTurns, use: options.use, } as GenerateOptions; } diff --git a/js/plugins/dotprompt/tests/prompt_test.ts b/js/plugins/dotprompt/tests/prompt_test.ts index 90cae459c..19577dc2c 100644 --- a/js/plugins/dotprompt/tests/prompt_test.ts +++ b/js/plugins/dotprompt/tests/prompt_test.ts @@ -134,10 +134,12 @@ describe('Prompt', () => { input: { name: 'Michael' }, onChunk: streamingCallback, returnToolRequests: true, + maxTurns: 17, use: middleware, }); assert.strictEqual(rendered.onChunk, streamingCallback); assert.strictEqual(rendered.returnToolRequests, true); + assert.strictEqual(rendered.maxTurns, 17); assert.strictEqual(rendered.use, middleware); }); @@ -449,12 +451,14 @@ describe('DotpromptRef', () => { input: { name: 'Charlie' }, onChunk: streamingCallback, returnToolRequests: true, + maxTurns: 17, }; const rendered = await ref.render(registry, options); assert.strictEqual(rendered.onChunk, streamingCallback); assert.strictEqual(rendered.returnToolRequests, true); + assert.strictEqual(rendered.maxTurns, 17); }); it('Should cache loaded prompt in DotpromptRef', async () => { From 24a42b233ee8aa4bb34f55c65c900aa2f7a14d25 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Fri, 13 Dec 2024 17:11:35 -0500 Subject: [PATCH 101/562] fix(js/plugins/googleai): work around googleai forced tool calling infinite loop (#1524) --- js/plugins/vertexai/src/gemini.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/js/plugins/vertexai/src/gemini.ts b/js/plugins/vertexai/src/gemini.ts index 4c45d7db9..b6208c248 100644 --- a/js/plugins/vertexai/src/gemini.ts +++ b/js/plugins/vertexai/src/gemini.ts @@ -510,7 +510,12 @@ export function defineGeminiModel( : []; let toolConfig: ToolConfig | undefined; - if (request?.config?.functionCallingConfig) { + if ( + request?.config?.functionCallingConfig && + // This is a workround for issue: https://github.com/firebase/genkit/issues/1520 + // TODO: remove this when the issue is resolved upstream in the Gemini API + !messages.at(-1)?.content.find((c) => c.toolResponse) + ) { toolConfig = { functionCallingConfig: { allowedFunctionNames: From b61da1f9cf4c1617dd73957882756e785b5b066f Mon Sep 17 00:00:00 2001 From: mjchristy Date: Fri, 13 Dec 2024 19:32:54 -0500 Subject: [PATCH 102/562] Adding a new issue template for Dev UI feature (#1502) * Adding a new template for Dev UI features --- .github/ISSUE_TEMPLATE/dev-ui-feature.md | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/dev-ui-feature.md diff --git a/.github/ISSUE_TEMPLATE/dev-ui-feature.md b/.github/ISSUE_TEMPLATE/dev-ui-feature.md new file mode 100644 index 000000000..a760857d2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/dev-ui-feature.md @@ -0,0 +1,29 @@ +--- +name: Dev UI feature request +about: Use this to request a feature in the Dev UI +title: "[Dev UI]" +labels: devui +assignees: '' + +--- + +## Overview +A clear and concise description of what the feature is. + +### User goal(s) +What is the user trying to accomplish with this feature? + +## Requirements +### Acceptance Criteria +- 1 +- 2 +- 3 + +### Designs + +Inlined screenshots + +## Notes + +Are there any open questions? Anything else + From da8feda432904f37311cab73183754081ff99f8b Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 16 Dec 2024 10:38:40 -0500 Subject: [PATCH 103/562] feat(js/plugins): added googleai/text-embedding-004 and vertexai/text-embedding-005 (#1525) --- js/plugins/googleai/src/embedder.ts | 13 +++++++++++++ js/plugins/googleai/src/index.ts | 2 ++ js/plugins/vertexai/src/embedder.ts | 2 ++ js/plugins/vertexai/src/index.ts | 2 ++ 4 files changed, 19 insertions(+) diff --git a/js/plugins/googleai/src/embedder.ts b/js/plugins/googleai/src/embedder.ts index 9e0818488..00adfb22f 100644 --- a/js/plugins/googleai/src/embedder.ts +++ b/js/plugins/googleai/src/embedder.ts @@ -52,8 +52,21 @@ export const textEmbeddingGecko001 = embedderRef({ }, }); +export const textEmbedding004 = embedderRef({ + name: 'googleai/text-embedding-004', + configSchema: GeminiEmbeddingConfigSchema, + info: { + dimensions: 768, + label: 'Google Gen AI - Text Embedding 001', + supports: { + input: ['text'], + }, + }, +}); + export const SUPPORTED_MODELS = { 'embedding-001': textEmbeddingGecko001, + 'text-embedding-004': textEmbedding004, }; export function defineGoogleAIEmbedder( diff --git a/js/plugins/googleai/src/index.ts b/js/plugins/googleai/src/index.ts index b9e6ea582..47bb6ae60 100644 --- a/js/plugins/googleai/src/index.ts +++ b/js/plugins/googleai/src/index.ts @@ -19,6 +19,7 @@ import { GenkitPlugin, genkitPlugin } from 'genkit/plugin'; import { SUPPORTED_MODELS as EMBEDDER_MODELS, defineGoogleAIEmbedder, + textEmbedding004, textEmbeddingGecko001, } from './embedder.js'; import { @@ -40,6 +41,7 @@ export { gemini15Flash8b, gemini15Pro, gemini20FlashExp, + textEmbedding004, textEmbeddingGecko001, }; diff --git a/js/plugins/vertexai/src/embedder.ts b/js/plugins/vertexai/src/embedder.ts index 40e7a0a21..28e8ad7ff 100644 --- a/js/plugins/vertexai/src/embedder.ts +++ b/js/plugins/vertexai/src/embedder.ts @@ -62,6 +62,7 @@ function commonRef( export const textEmbeddingGecko003 = commonRef('textembedding-gecko@003'); export const textEmbedding004 = commonRef('text-embedding-004'); +export const textEmbedding005 = commonRef('text-embedding-005'); export const textEmbeddingGeckoMultilingual001 = commonRef( 'textembedding-gecko-multilingual@001' ); @@ -72,6 +73,7 @@ export const textMultilingualEmbedding002 = commonRef( export const SUPPORTED_EMBEDDER_MODELS: Record = { 'textembedding-gecko@003': textEmbeddingGecko003, 'text-embedding-004': textEmbedding004, + 'text-embedding-005': textEmbedding005, 'textembedding-gecko-multilingual@001': textEmbeddingGeckoMultilingual001, 'text-multilingual-embedding-002': textMultilingualEmbedding002, // TODO: add support for multimodal embeddings diff --git a/js/plugins/vertexai/src/index.ts b/js/plugins/vertexai/src/index.ts index 6648ca686..a53bdb6ba 100644 --- a/js/plugins/vertexai/src/index.ts +++ b/js/plugins/vertexai/src/index.ts @@ -22,6 +22,7 @@ import { SUPPORTED_EMBEDDER_MODELS, defineVertexAIEmbedder, textEmbedding004, + textEmbedding005, textEmbeddingGecko003, textEmbeddingGeckoMultilingual001, textMultilingualEmbedding002, @@ -51,6 +52,7 @@ export { imagen3, imagen3Fast, textEmbedding004, + textEmbedding005, textEmbeddingGecko003, textEmbeddingGeckoMultilingual001, textMultilingualEmbedding002, From dad77cc3bf1767272f65378b3c6d5a96dd539fdd Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 16 Dec 2024 10:53:06 -0500 Subject: [PATCH 104/562] chore: update release_main.sh made it more configurable --- scripts/release_main.sh | 44 +++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/scripts/release_main.sh b/scripts/release_main.sh index f9b2bf970..1df0491da 100755 --- a/scripts/release_main.sh +++ b/scripts/release_main.sh @@ -10,79 +10,85 @@ # pnpm login --registry https://wombat-dressing-room.appspot.com CURRENT=`pwd` +RELEASE_BRANCH="${RELEASE_BRANCH:-main}" +RELEASE_TAG="${RELEASE_TAG:-next}" cd genkit-tools/common -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd genkit-tools/telemetry-server -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd genkit-tools/cli -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/core -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/ai -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/genkit -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/plugins/dotprompt -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/plugins/chroma -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/plugins/dev-local-vectorstore -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/plugins/firebase -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/plugins/google-cloud -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/plugins/googleai -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/plugins/ollama -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/plugins/pinecone -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/plugins/vertexai -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/plugins/evaluators -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/plugins/langchain -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/plugins/checks -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT cd js/plugins/mcp -pnpm publish --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com +cd $CURRENT + +cd js/plugins/express +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT From c23b9bafa30dd283576b543c7d09423cd9046b26 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 16 Dec 2024 11:00:44 -0500 Subject: [PATCH 105/562] core: update release_js_main.yml make tag configurable and make it recognize current branch flip it to test mode for testing --- .github/workflows/release_js_main.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release_js_main.yml b/.github/workflows/release_js_main.yml index f7106c799..cfaf4c237 100644 --- a/.github/workflows/release_js_main.yml +++ b/.github/workflows/release_js_main.yml @@ -16,7 +16,16 @@ name: Release Genkit JS (latest) on: workflow_dispatch: - + inputs: + releaseTag: + description: Release tag + type: choice + default: next + required: true + options: + - main + - next + jobs: build: name: Run build tasks @@ -29,6 +38,10 @@ jobs: with: node-version: 21.x cache: 'pnpm' + - name: Extract branch name + shell: bash + run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT + id: extract_branch - name: Install dependencies run: pnpm install - name: Run build script @@ -44,6 +57,7 @@ jobs: cache: 'pnpm' registry-url: 'https://wombat-dressing-room.appspot.com/' - name: release script - run: scripts/release_main.sh + shell: bash + run: echo RELEASE_BRANCH=${{ steps.extract_branch.outputs.branch }} RELEASE_TAG=${{ inputs.releaseTag }} scripts/release_main.sh env: NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} From 378b5c4f53772c29234dcd2084fc87e36c8a7f94 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 16 Dec 2024 11:08:54 -0500 Subject: [PATCH 106/562] chore: update release_main.sh flip it to test mode --- scripts/release_main.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/release_main.sh b/scripts/release_main.sh index 1df0491da..a28fc953d 100755 --- a/scripts/release_main.sh +++ b/scripts/release_main.sh @@ -14,9 +14,11 @@ RELEASE_BRANCH="${RELEASE_BRANCH:-main}" RELEASE_TAG="${RELEASE_TAG:-next}" cd genkit-tools/common -pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com +echo pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT +exit 0 + cd genkit-tools/telemetry-server pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT From 42fdb097969414cdef325c97ce259ca298e03d7d Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 16 Dec 2024 11:10:20 -0500 Subject: [PATCH 107/562] chore: update release_js_main.yml undo test mode, title update, s/main/latest/ for tag --- .github/workflows/release_js_main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release_js_main.yml b/.github/workflows/release_js_main.yml index cfaf4c237..31590b334 100644 --- a/.github/workflows/release_js_main.yml +++ b/.github/workflows/release_js_main.yml @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -name: Release Genkit JS (latest) +name: Release Genkit JS on: workflow_dispatch: @@ -23,8 +23,8 @@ on: default: next required: true options: - - main - next + - latest jobs: build: @@ -58,6 +58,6 @@ jobs: registry-url: 'https://wombat-dressing-room.appspot.com/' - name: release script shell: bash - run: echo RELEASE_BRANCH=${{ steps.extract_branch.outputs.branch }} RELEASE_TAG=${{ inputs.releaseTag }} scripts/release_main.sh + run: RELEASE_BRANCH=${{ steps.extract_branch.outputs.branch }} RELEASE_TAG=${{ inputs.releaseTag }} scripts/release_main.sh env: NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} From 65021c5c9de78b0625544420c8e0dc468172698c Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 16 Dec 2024 11:17:39 -0500 Subject: [PATCH 108/562] chore: update release_main.sh undo test mode --- scripts/release_main.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/release_main.sh b/scripts/release_main.sh index a28fc953d..1df0491da 100755 --- a/scripts/release_main.sh +++ b/scripts/release_main.sh @@ -14,11 +14,9 @@ RELEASE_BRANCH="${RELEASE_BRANCH:-main}" RELEASE_TAG="${RELEASE_TAG:-next}" cd genkit-tools/common -echo pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com +pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT -exit 0 - cd genkit-tools/telemetry-server pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT From 1b31319a28cf5f38122c28e446e590ba4c0077cf Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 16 Dec 2024 11:22:06 -0500 Subject: [PATCH 109/562] chore: added plugins to bump-js-version.yml --- .github/workflows/bump-js-version.yml | 156 ++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/.github/workflows/bump-js-version.yml b/.github/workflows/bump-js-version.yml index 20ae85aba..6b8025aff 100644 --- a/.github/workflows/bump-js-version.yml +++ b/.github/workflows/bump-js-version.yml @@ -50,6 +50,7 @@ jobs: with: node-version: 20.x cache: 'pnpm' + # bump core libs - name: 'js/core version bump' uses: 'phips28/gh-action-bump-version@master' env: @@ -83,3 +84,158 @@ jobs: preid: ${{ inputs.preid }} commit-message: 'chore: bump genkit version to {{version}}' tag-prefix: 'genkit@' + # bump plugins + - name: 'js/plugins/chroma version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/chroma + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump genkitx-chromadb version to {{version}}' + tag-prefix: 'genkitx-chromadb' + - name: 'js/plugins/dev-local-vectorstore version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/dev-local-vectorstore + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump @genkit-ai/dev-local-vectorstore version to {{version}}' + tag-prefix: '@genkit-ai/dev-local-vectorstore@' + - name: 'js/plugins/dotprompt version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/dotprompt + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump @genkit-ai/dotprompt version to {{version}}' + tag-prefix: '@genkit-ai/dotprompt@' + - name: 'js/plugins/evaluators version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/evaluators + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump @genkit-ai/evaluator version to {{version}}' + tag-prefix: '@genkit-ai/evaluator@' + - name: 'js/plugins/firebase version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/firebase + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump @genkit-ai/firebase version to {{version}}' + tag-prefix: '@genkit-ai/firebase@' + - name: 'js/plugins/google-cloud version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/google-cloud + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump @genkit-ai/google-cloud version to {{version}}' + tag-prefix: '@genkit-ai/google-cloud@' + - name: 'js/plugins/googleai version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/googleai + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump @genkit-ai/googleai version to {{version}}' + tag-prefix: '@genkit-ai/googleai@' + - name: 'js/plugins/langchain version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/langchain + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump genkitx-langchain version to {{version}}' + tag-prefix: 'genkitx-langchain@' + - name: 'js/plugins/ollama version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/ollama + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump genkitx-ollama version to {{version}}' + tag-prefix: 'genkitx-ollama@' + - name: 'js/plugins/pinecone version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/pinecone + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump genkitx-pinecone version to {{version}}' + tag-prefix: 'genkitx-pinecone@' + - name: 'js/plugins/vertexai version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/vertexai + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump @genkit-ai/vertexai version to {{version}}' + tag-prefix: '@genkit-ai/vertexai@' + - name: 'js/plugins/checks version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/checks + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump @genkit-ai/checks version to {{version}}' + tag-prefix: '@genkit-ai/checks@' + - name: 'js/plugins/mcp version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/mcp + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump genkitx-mcp version to {{version}}' + tag-prefix: 'genkitx-mcp@' + - name: 'js/plugins/express version bump' + uses: 'phips28/gh-action-bump-version@master' + env: + GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} + PACKAGEJSON_DIR: js/plugins/express + with: + default: ${{ inputs.releaseType }} + version-type: ${{ inputs.releaseType }} + preid: ${{ inputs.preid }} + commit-message: 'chore: bump @genkit-ai/express version to {{version}}' + tag-prefix: '@genkit-ai/express@' From ce6d0de52739bbba3b0ae57158d95c2fe1ce0d3e Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 16 Dec 2024 11:22:46 -0500 Subject: [PATCH 110/562] chore: Delete .github/workflows/bump-js-plugins-version.yml --- .github/workflows/bump-js-plugins-version.yml | 195 ------------------ 1 file changed, 195 deletions(-) delete mode 100644 .github/workflows/bump-js-plugins-version.yml diff --git a/.github/workflows/bump-js-plugins-version.yml b/.github/workflows/bump-js-plugins-version.yml deleted file mode 100644 index a6e5a078b..000000000 --- a/.github/workflows/bump-js-plugins-version.yml +++ /dev/null @@ -1,195 +0,0 @@ -# Copyright 2024 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. - -name: Bump and Tag JS Plugins Version - -on: - workflow_dispatch: - inputs: - releaseType: - description: Release type - type: choice - default: patch - required: true - options: - - patch - - minor - - major - - prerelease - - premajor - - preminor - - prepatch - preid: - description: Prerelease identifier - type: string - default: rc - required: true - -jobs: - build: - name: Version bump - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - token: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - - uses: pnpm/action-setup@v3 - - name: Set up node v20 - uses: actions/setup-node@v4 - with: - node-version: 20.x - cache: 'pnpm' - - name: 'js/plugins/chroma version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/chroma - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump genkitx-chromadb version to {{version}}' - tag-prefix: 'genkitx-chromadb' - - name: 'js/plugins/dev-local-vectorstore version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/dev-local-vectorstore - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump @genkit-ai/dev-local-vectorstore version to {{version}}' - tag-prefix: '@genkit-ai/dev-local-vectorstore@' - - name: 'js/plugins/dotprompt version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/dotprompt - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump @genkit-ai/dotprompt version to {{version}}' - tag-prefix: '@genkit-ai/dotprompt@' - - name: 'js/plugins/evaluators version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/evaluators - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump @genkit-ai/evaluator version to {{version}}' - tag-prefix: '@genkit-ai/evaluator@' - - name: 'js/plugins/firebase version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/firebase - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump @genkit-ai/firebase version to {{version}}' - tag-prefix: '@genkit-ai/firebase@' - - name: 'js/plugins/google-cloud version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/google-cloud - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump @genkit-ai/google-cloud version to {{version}}' - tag-prefix: '@genkit-ai/google-cloud@' - - name: 'js/plugins/googleai version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/googleai - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump @genkit-ai/googleai version to {{version}}' - tag-prefix: '@genkit-ai/googleai@' - - name: 'js/plugins/langchain version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/langchain - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump genkitx-langchain version to {{version}}' - tag-prefix: 'genkitx-langchain@' - - name: 'js/plugins/ollama version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/ollama - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump genkitx-ollama version to {{version}}' - tag-prefix: 'genkitx-ollama@' - - name: 'js/plugins/pinecone version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/pinecone - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump genkitx-pinecone version to {{version}}' - tag-prefix: 'genkitx-pinecone@' - - name: 'js/plugins/vertexai version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/vertexai - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump @genkit-ai/vertexai version to {{version}}' - tag-prefix: '@genkit-ai/vertexai@' - - name: 'js/plugins/checks version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/checks - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump @genkit-ai/checks version to {{version}}' - tag-prefix: '@genkit-ai/checks@' - - name: 'js/plugins/mcp version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/mcp - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump genkitx-mcp version to {{version}}' - tag-prefix: 'genkitx-mcp@' From 40b6f4d98fcd37c5749dc97749e598867e643d3f Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 16 Dec 2024 11:24:00 -0500 Subject: [PATCH 111/562] chore: fixed express plugin version --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index 8edd01041..f1f8fc177 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "0.9.6", + "version": "1.0.0-rc.0", "type": "commonjs", "scripts": { "check": "tsc", From 528cf1ebb363a30bb5069e4713249bcc329754d8 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:24:44 +0000 Subject: [PATCH 112/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.1 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 6a73f95db..acb8bd8bb 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 19f1f8e25912329ab221a8f614ba7edf0ad0012f Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:24:46 +0000 Subject: [PATCH 113/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.1 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 5e67c2a6d..8fb86a044 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From 067cc21f629ad0835dfe8e2d186f3d083d26d60c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:24:49 +0000 Subject: [PATCH 114/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.1 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index 2aeadeb38..e34229fba 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From 0096137889cbc9005798e4dc48baeba5550b4065 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 16 Dec 2024 11:25:47 -0500 Subject: [PATCH 115/562] chore: update bump-cli-version.yml --- .github/workflows/bump-cli-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bump-cli-version.yml b/.github/workflows/bump-cli-version.yml index 34f13cfae..ff59e7548 100644 --- a/.github/workflows/bump-cli-version.yml +++ b/.github/workflows/bump-cli-version.yml @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -name: Bump CLI Version +name: Bump and Tag CLI Version on: workflow_dispatch: From 8c140983fa3f68df58dd756c302481a7cdcca15d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:26:29 +0000 Subject: [PATCH 116/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.1 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 2cdbf65a0..090e39d06 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From 7270093984b817a7cb950c1b49063417530d66ed Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:26:32 +0000 Subject: [PATCH 117/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.1 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index ebf1881be..0e4b82b74 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From 1e998ea682ad7c4d3fd001f48da2e63a5585cbf2 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:26:35 +0000 Subject: [PATCH 118/562] chore: bump genkit version to genkit@1.0.0-rc.1 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index e77a53f43..53c1fb7aa 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From 6649c7063fdae9b646eb7b1534ddd30fdf09e120 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:26:40 +0000 Subject: [PATCH 119/562] chore: bump genkitx-chromadb version to genkitx-chromadb1.0.0-rc.1 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index 21361edb1..0ac50dd20 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From 2c49e0c3252f8e05d330eae9b93bbd9343cf9e43 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:26:45 +0000 Subject: [PATCH 120/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.1 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 3ade10527..91e7a6e1b 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From 2c33734cd332ef36858042522c6c9b738ec41e34 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:26:47 +0000 Subject: [PATCH 121/562] chore: bump @genkit-ai/dotprompt version to @genkit-ai/dotprompt@1.0.0-rc.1 --- js/plugins/dotprompt/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dotprompt/package.json b/js/plugins/dotprompt/package.json index 777aaf00b..3d4349e52 100644 --- a/js/plugins/dotprompt/package.json +++ b/js/plugins/dotprompt/package.json @@ -9,7 +9,7 @@ "prompting", "templating" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From 8e44857e743f7f3821c7661fd8654bd2d6637ee8 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:26:50 +0000 Subject: [PATCH 122/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.1 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 3e81073dd..89b7af74b 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From 5e83e570c80d6adb459b1c106f36d8bfc81ee329 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:26:53 +0000 Subject: [PATCH 123/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.1 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 53443f42a..60780938f 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From 091697d069b37e3ab7726c754c3eabc595c05943 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:26:58 +0000 Subject: [PATCH 124/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.1 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index 50ef025ad..35f4b5862 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From 64fed06731f8dc9970c5437508187efc1c29a48d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:27:01 +0000 Subject: [PATCH 125/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.1 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index ed4b72265..5f30fd536 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From ca9c1e067b26a571195a29683b724812cf6d0f62 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:27:08 +0000 Subject: [PATCH 126/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.1 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index f9ce5594c..ad26dc688 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From 0aa1019090f790e2056e994a301fa489a3eeef7d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:27:11 +0000 Subject: [PATCH 127/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.1 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index fe8af69ef..6a36f0202 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From 7483d75c756a7806bb894835cdcc40b51a310c96 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:27:17 +0000 Subject: [PATCH 128/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.1 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index 8fdd8681f..e21290fca 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From bbc363ae47b1c899646cdc809c288e9a88eb7e0d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:27:22 +0000 Subject: [PATCH 129/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.1 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 8a80e57fa..952340062 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From 2f749c2cac83fd41cefce0c4468a37c2ed1ab6a6 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:27:25 +0000 Subject: [PATCH 130/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.1 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index e54a4df86..d8245d403 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From 12d12a82d089888ed74ab1b92bf5126d2ea8ef4a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:27:30 +0000 Subject: [PATCH 131/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.1 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index 2db8a4dc4..0d61ef4a0 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From 4da00260eae79260d0fc3d8bb55d142c1145f638 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 16 Dec 2024 16:27:35 +0000 Subject: [PATCH 132/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.1 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index f1f8fc177..73d077b95 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.0", + "version": "1.0.0-rc.1", "type": "commonjs", "scripts": { "check": "tsc", From 2238a15606f7398d8667cb8c46e02d55593c242f Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 16 Dec 2024 11:28:57 -0500 Subject: [PATCH 133/562] chore: fixed chromadb tag prefix --- .github/workflows/bump-js-version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bump-js-version.yml b/.github/workflows/bump-js-version.yml index 6b8025aff..345c64b4a 100644 --- a/.github/workflows/bump-js-version.yml +++ b/.github/workflows/bump-js-version.yml @@ -95,7 +95,7 @@ jobs: version-type: ${{ inputs.releaseType }} preid: ${{ inputs.preid }} commit-message: 'chore: bump genkitx-chromadb version to {{version}}' - tag-prefix: 'genkitx-chromadb' + tag-prefix: 'genkitx-chromadb@' - name: 'js/plugins/dev-local-vectorstore version bump' uses: 'phips28/gh-action-bump-version@master' env: From 49266ce94e4e6dc6090ccfef0767707c0d0459d9 Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:40:32 +0000 Subject: [PATCH 134/562] docs: update contrib.md (#1528) --- CONTRIBUTING.md | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 780626318..a9af80887 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,7 +32,9 @@ use GitHub pull requests for this purpose. Consult [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on using pull requests. -## Environment +## Setup + +### Environment 1. [Install node v20](https://nodejs.org/en/download) 2. Run `corepack enable pnpm` to enable pnpm. @@ -40,7 +42,7 @@ information on using pull requests. Note: We recommend using Node v20 or greater when compiling and running Genkit. Any older versions of Node may not work properly. -## Quick setup +### Install dependencies ``` pnpm i @@ -49,7 +51,7 @@ pnpm run setup This will install all dependencies and build all packages. -## Build it +### Build it ``` pnpm build @@ -57,7 +59,7 @@ pnpm build This will build all packages in this repository. This is recommended the first time you check out a fresh repo or pull new revisions. -## Pack it +### Pack it Pack packages for testing/distribution. @@ -69,7 +71,7 @@ pnpm pack:all This will produce tarballs in the `dist` folder. Also `genkit-dist.zip` -- a zip of all the package tarballs. -## Link it +### Link it You will need the Genkit CLI to run test apps and the Developer UI (this is done for you with `pnpm run setup`): @@ -92,7 +94,7 @@ genkit start -- tsx --watch src/index.ts Point your browser to http://localhost:4000 -### Run evaluations +### Run an evaluation We'll be using the `pdfQA` flow for our example. @@ -101,24 +103,24 @@ To start, let's make sure we have some context to pull from the vector store. 1. Start the Developer UI ``` -cd js/testapps/rag -genkit start -- tsx --watch src/index.ts +cd js/testapps/evals +genkit start -- pnpm genkit:dev ``` 2. Click on the `indexPdf` flow in the left nav. 3. Input paths to pdfs you want to index. There's one checked into the directory: ``` -"./35650.pdf" +"./docs/cat-handbook.pdf" ``` -4. Run the evaluation +4. Run an evaluation on a sample dataset checked into the testapp ``` -genkit eval:flow pdfQA '"What's a brief description of MapReduce?"' +genkit eval:flow pdfQA --input ./data/cat_adoption_questions.json ``` -5. To see the output, look for the log line `Saving EvalRun` with the path to the json file. +5. Go to the **Evaluations** tab in the Developer UI to view the evaluation results. ## Code it @@ -146,6 +148,16 @@ pnpm build:watch in the package that you're editing. +## Document it + +If you are contributing to the core Genkit JS SDK (under the `/js` directory), please make sure that all exported members have a valid [JSDoc](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) associated with them. + +Use the following command to build and view the new API reference locally. + +``` +cd js && pnpm build && pnpm typedoc-html && open api-refs-js/index.html +``` + ## Send it Once done coding you will want to send a PR. Always do things in a separate branch (by convention name the branch `your_name-feature-something`). From 65ef534d00a8a7ebfc91f98dedb30fb463ac3eb8 Mon Sep 17 00:00:00 2001 From: ifielker Date: Mon, 16 Dec 2024 13:31:24 -0500 Subject: [PATCH 135/562] docs: pinecone docs (#1475) --- js/plugins/pinecone/src/index.ts | 83 +++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 7 deletions(-) diff --git a/js/plugins/pinecone/src/index.ts b/js/plugins/pinecone/src/index.ts index 33548a9a7..54268430b 100644 --- a/js/plugins/pinecone/src/index.ts +++ b/js/plugins/pinecone/src/index.ts @@ -59,8 +59,16 @@ const PineconeIndexerOptionsSchema = z.object({ namespace: z.string().optional(), }); -const TEXT_KEY = '_content'; +const CONTENT_KEY = '_content'; +/** + * pineconeRetrieverRef function creates a retriever for Pinecone. + * @param params The params for the new Pinecone retriever + * @param params.indexId The indexId for the Pinecone retriever + * @param params.displayName A display name for the retriever. +If not specified, the default label will be `Pinecone - ` + * @returns A reference to a Pinecone retriever. + */ export const pineconeRetrieverRef = (params: { indexId: string; displayName?: string; @@ -74,6 +82,14 @@ export const pineconeRetrieverRef = (params: { }); }; +/** + * pineconeIndexerRef function creates an indexer for Pinecone. + * @param params The params for the new Pinecone indexer. + * @param params.indexId The indexId for the Pinecone indexer. + * @param params.displayName A display name for the indexer. +If not specified, the default label will be `Pinecone - ` + * @returns A reference to a Pinecone indexer. + */ export const pineconeIndexerRef = (params: { indexId: string; displayName?: string; @@ -88,12 +104,21 @@ export const pineconeIndexerRef = (params: { }; /** - * Pinecone plugin that provides a pinecone retriever and indexer + * Pinecone plugin that provides a Pinecone retriever and indexer + * @param params An array of params to set up Pinecone retrievers and indexers + * @param params.clientParams PineconeConfiguration containing the +PINECONE_API_KEY. If not set, the PINECONE_API_KEY environment variable will +be used instead. + * @param params.indexId The name of the index + * @param params.embedder The embedder to use for the indexer and retriever + * @param params.embedderOptions Options to customize the embedder + * @returns The Pinecone Genkit plugin */ export function pinecone( params: { clientParams?: PineconeConfiguration; indexId: string; + contentKey?: string; embedder: EmbedderArgument; embedderOptions?: z.infer; }[] @@ -108,6 +133,18 @@ export default pinecone; /** * Configures a Pinecone retriever. + * @param ai A Genkit instance + * @param params The params for the retriever + * @param params.indexId The name of the retriever + * @param params.clientParams PineconeConfiguration containing the +PINECONE_API_KEY. If not set, the PINECONE_API_KEY environment variable will +be used instead. + * @param params.textKey Deprecated. Please use contentKey. + * @param params.contentKey The metadata key that contains the +content. If not specified, the value '_content' is used by default. + * @param params.embedder The embedder to use for the retriever + * @param params.embedderOptions Options to customize the embedder + * @returns A Pinecone retriever */ export function configurePineconeRetriever< EmbedderCustomOptions extends z.ZodTypeAny, @@ -116,7 +153,11 @@ export function configurePineconeRetriever< params: { indexId: string; clientParams?: PineconeConfiguration; + /** + * @deprecated use contentKey instead. + */ textKey?: string; + contentKey?: string; embedder: EmbedderArgument; embedderOptions?: z.infer; } @@ -125,7 +166,7 @@ export function configurePineconeRetriever< ...params, }; const pineconeConfig = params.clientParams ?? getDefaultConfig(); - const textKey = params.textKey ?? TEXT_KEY; + const contentKey = params.contentKey ?? params.textKey ?? CONTENT_KEY; const pinecone = new Pinecone(pineconeConfig); const index = pinecone.index(indexId); @@ -155,8 +196,8 @@ export function configurePineconeRetriever< .filter((m): m is RecordMetadata => !!m) .map((m) => { const metadata = m; - const content = metadata[textKey] as string; - delete metadata[textKey]; + const content = metadata[contentKey] as string; + delete metadata[contentKey]; return Document.fromText(content, metadata).toJSON(); }), }; @@ -166,6 +207,18 @@ export function configurePineconeRetriever< /** * Configures a Pinecone indexer. + * @param ai A Genkit instance + * @param params The params for the indexer + * @param params.indexId The name of the indexer + * @param params.clientParams PineconeConfiguration containing the +PINECONE_API_KEY. If not set, the PINECONE_API_KEY environment variable will +be used instead. + * @param params.textKey Deprecated. Please use contentKey. + * @param params.contentKey The metadata key that contains the +content. If not specified, the value '_content' is used by default. + * @param params.embedder The embedder to use for the retriever + * @param params.embedderOptions Options to customize the embedder + * @returns A Genkit indexer */ export function configurePineconeIndexer< EmbedderCustomOptions extends z.ZodTypeAny, @@ -174,7 +227,11 @@ export function configurePineconeIndexer< params: { indexId: string; clientParams?: PineconeConfiguration; + /** + * @deprecated use contentKey instead. + */ textKey?: string; + contentKey?: string; embedder: EmbedderArgument; embedderOptions?: z.infer; } @@ -183,7 +240,7 @@ export function configurePineconeIndexer< ...params, }; const pineconeConfig = params.clientParams ?? getDefaultConfig(); - const textKey = params.textKey ?? TEXT_KEY; + const contentKey = params.contentKey ?? params.textKey ?? CONTENT_KEY; const pinecone = new Pinecone(pineconeConfig); const index = pinecone.index(indexId); @@ -212,7 +269,7 @@ export function configurePineconeIndexer< ...docs[i].metadata, }; - metadata[textKey] = docs[i].text; + metadata[contentKey] = docs[i].text; const id = Md5.hashStr(JSON.stringify(docs[i])); return { id, @@ -227,6 +284,10 @@ export function configurePineconeIndexer< /** * Helper function for creating a Pinecone index. + * @param params The params for creating a Pinecone index + * @param params.clientParams The params to initialize Pinecone. + * @param params.options The options for creating the index. + * @returns A Pinecone index. */ export async function createPineconeIndex(params: { clientParams?: PineconeConfiguration; @@ -239,6 +300,10 @@ export async function createPineconeIndex(params: { /** * Helper function to describe a Pinecone index. Use it to check if a newly created index is ready for use. + * @param params The params for describing a Pinecone index. + * @param params.clientParams The params to initialize Pinecone. + * @param params.name The name of the Pinecone index to describe. + * @return A description of the Pinecone index. */ export async function describePineconeIndex(params: { clientParams?: PineconeConfiguration; @@ -251,6 +316,10 @@ export async function describePineconeIndex(params: { /** * Helper function for deleting Chroma collections. + * @param params The params for deleting a Pinecone index. + * @param params.clientParams The params to initialize Pinecone. + * @param params.name The name of the Pinecone index to delete. + * @returns a void Promise that is fulfilled when the index has been deleted. */ export async function deletePineconeIndex(params: { clientParams?: PineconeConfiguration; From 67b61514c24fc192efe46e8875faf2eafeef2817 Mon Sep 17 00:00:00 2001 From: jiiyaaa__ <66894607+river0g@users.noreply.github.com> Date: Tue, 17 Dec 2024 06:10:28 +0900 Subject: [PATCH 136/562] feat(js/plugins): added custom dimensions parameter to text-embedding (#1529) --- js/plugins/googleai/src/embedder.ts | 9 +++++++++ js/plugins/vertexai/src/embedder.ts | 11 ++++++++++- js/plugins/vertexai/src/predict.ts | 6 +++--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/js/plugins/googleai/src/embedder.ts b/js/plugins/googleai/src/embedder.ts index 00adfb22f..31e27afa0 100644 --- a/js/plugins/googleai/src/embedder.ts +++ b/js/plugins/googleai/src/embedder.ts @@ -36,6 +36,14 @@ export const GeminiEmbeddingConfigSchema = z.object({ taskType: TaskTypeSchema.optional(), title: z.string().optional(), version: z.string().optional(), + /** + * The `outputDimensionality` parameter allows you to specify the dimensionality of the embedding output. + * By default, the model generates embeddings with 768 dimensions. Models such as + * `text-embedding-004`, `text-embedding-005`, and `text-multilingual-embedding-002` + * allow the output dimensionality to be adjusted between 1 and 768. + * By selecting a smaller output dimensionality, users can save memory and storage space, leading to more efficient computations. + **/ + outputDimensionality: z.number().min(1).max(768).optional(), }); export type GeminiEmbeddingConfig = z.infer; @@ -122,6 +130,7 @@ export function defineGoogleAIEmbedder( role: '', parts: [{ text: doc.text }], }, + outputDimensionality: options?.outputDimensionality, } as EmbedContentRequest); const values = response.embedding.values; return { embedding: values }; diff --git a/js/plugins/vertexai/src/embedder.ts b/js/plugins/vertexai/src/embedder.ts index 28e8ad7ff..8e81599ac 100644 --- a/js/plugins/vertexai/src/embedder.ts +++ b/js/plugins/vertexai/src/embedder.ts @@ -39,6 +39,14 @@ export const VertexEmbeddingConfigSchema = z.object({ title: z.string().optional(), location: z.string().optional(), version: z.string().optional(), + /** + * The `outputDimensionality` parameter allows you to specify the dimensionality of the embedding output. + * By default, the model generates embeddings with 768 dimensions. Models such as + * `text-embedding-004`, `text-embedding-005`, and `text-multilingual-embedding-002` + * allow the output dimensionality to be adjusted between 1 and 768. + * By selecting a smaller output dimensionality, users can save memory and storage space, leading to more efficient computations. + **/ + outputDimensionality: z.number().min(1).max(768).optional(), }); export type VertexEmbeddingConfig = z.infer; @@ -145,7 +153,8 @@ export function defineVertexAIEmbedder( task_type: options?.taskType, title: options?.title, }; - }) + }), + { outputDimensionality: options?.outputDimensionality } ); return { embeddings: response.predictions.map((p) => ({ diff --git a/js/plugins/vertexai/src/predict.ts b/js/plugins/vertexai/src/predict.ts index 0d4f39302..93bf8277c 100644 --- a/js/plugins/vertexai/src/predict.ts +++ b/js/plugins/vertexai/src/predict.ts @@ -33,7 +33,7 @@ interface PredictionResponse { export type PredictClient = ( instances: I[], - parameters?: P + parameters: P ) => Promise>; export function predictModel( @@ -43,14 +43,14 @@ export function predictModel( ): PredictClient { return async ( instances: I[], - parameters?: P + parameters: P ): Promise> => { const fetch = (await import('node-fetch')).default; const accessToken = await auth.getAccessToken(); const req = { instances, - parameters: parameters || {}, + parameters, }; const response = await fetch( From 9643e44ee1f7d9000e1d24a0688f7501367ff7e2 Mon Sep 17 00:00:00 2001 From: huangjeff5 <64040981+huangjeff5@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:46:02 -0600 Subject: [PATCH 137/562] chore: polish multi-agent example (#1476) --- samples/js-schoolAgent/README.md | 104 +++++++++++++++- samples/js-schoolAgent/src/attendanceAgent.ts | 24 +++- samples/js-schoolAgent/src/data.ts | 32 +++++ samples/js-schoolAgent/src/gradesAgent.ts | 53 ++++++++ samples/js-schoolAgent/src/infoAgent.ts | 37 ------ samples/js-schoolAgent/src/routingAgent.ts | 50 ++++++++ samples/js-schoolAgent/src/terminal.ts | 114 ++++++++++++------ samples/js-schoolAgent/src/tools.ts | 49 ++++++-- samples/js-schoolAgent/src/util.ts | 20 +++ 9 files changed, 387 insertions(+), 96 deletions(-) create mode 100644 samples/js-schoolAgent/src/gradesAgent.ts delete mode 100644 samples/js-schoolAgent/src/infoAgent.ts create mode 100644 samples/js-schoolAgent/src/routingAgent.ts create mode 100644 samples/js-schoolAgent/src/util.ts diff --git a/samples/js-schoolAgent/README.md b/samples/js-schoolAgent/README.md index 15b514d37..35d91f3ab 100644 --- a/samples/js-schoolAgent/README.md +++ b/samples/js-schoolAgent/README.md @@ -1,11 +1,107 @@ -## School Agent sample +# School Agent Sample -Get started: +A demonstration of a conversational, multi-agent assistant for a school system using GenKit and Google's Gemini Pro. This agent helps parents with attendance reporting and school information queries. + +In this example we have a RoutingAgent which is the main, general-purpose agent. +This agent comes equipped with additional specialized agents, that it can hand-off to as needed. + +These specialized agents are represented as prompts and embedded as tools to the original agent. + +## Agent Tools & Capabilities + +- **Agent Structure**: + - `RoutingAgent`: Main entry point and router, handling general queries and delegating to specialized agents + - `AttendanceAgent`: Specialized agent for absence/tardy reporting + - `GradesAgent`: Manages grade-related inquiries and academic performance + +Each specialized agent has its own set of tools that are only accessible to that specific agent: + +- **AttendanceAgent**: + - `reportAbsence`: Submit absence notifications + - `reportTardy`: Report late arrivals +- **GradesAgent**: + - `getRecentGrades`: Retrieve latest grade information + +The main RoutingAgent cannot directly access these specialized tools - it can only access its own tools and delegate to the specialized agents. This means the specialized agent descriptions need to clearly communicate their capabilities, since the main agent relies on these descriptions for appropriate routing. + +For example, when the RoutingAgent sees a grade-related query, it needs to know from the GradesAgent's description that it can handle grade lookups, even though it can't directly see the `getRecentGrades` tool. + +This architectural pattern: + +- Maintains clear separation of concerns +- Allows specialized agents to evolve independently +- Allows scaling up to a larger number of tools + +NOTE: The agent description is how the generalized agent knows what tools the specialized agent has available. An agent description that is too general may cause the routing agent to mess up by not knowing that a certain functionality was actually available. + +## Prerequisites + +- Node.js and genkit CLI installed +- Google AI API key + +## Getting Started + +1. Install dependencies: + +```bash +npm install +``` + +2. Set up your Google AI API key: ```bash -npm i +export GOOGLE_GENAI_API_KEY=your_api_key_here +``` -export GOOGLE_GENAI_API_KEY=... +3. Start the development server: +```bash npm run genkit:dev ``` + +In your terminal, a commandline chat interface should show up: + +``` +Telemetry API running on http://localhost:4033 +Genkit Developer UI: http://localhost:4000 + +> school-agent@1.0.0 dev +> tsx --no-warnings --watch src/terminal.ts + +bell> Hi there, my name is Bell and I'm here to help! 👋🎉 I'm your friendly AI assistant for parents of Sparkyville High School. I can answer your questions about the school, events, grades, and more. Just ask me! 😊 + +prompt> [insert your chats here] +``` + +You can feel free to tweak the sample. The project builds in watch mode, so any changes will be picked up immediately and should restart the conversation. + +## Usage + +The agent uses a multi-agent architecture: + +- Routing Agent: Acts as the main entry point and router, handling general queries while delegating specialized requests to appropriate agents +- Attendance Agent: Specialized agent focused on absence and tardy reporting +- Grades Agent: Manages academic performance queries, grade reports, and transcript requests + +Example queries: + +- "Evelyn will be late today" +- "What are the upcoming holidays I should be aware of?" +- "Show me my child's current grades" + +## Development + +- `npm run dev` - Run in development mode with hot reloading +- `npm run build` - Build the project +- `npm start` - Run the built version + +## Project Structure + +- `src/` + - `agents/` + - `routingAgent.ts` - Main agent that uses other agents as tools + - `attendanceAgent.ts` - Specialized attendance agent + - `gradesAgent.ts` - Academic performance and grades agent + - `tools.ts` - Tool definitions + - `types.ts` - TypeScript types + - `data.ts` - Sample data diff --git a/samples/js-schoolAgent/src/attendanceAgent.ts b/samples/js-schoolAgent/src/attendanceAgent.ts index e1163a482..044aefd7c 100644 --- a/samples/js-schoolAgent/src/attendanceAgent.ts +++ b/samples/js-schoolAgent/src/attendanceAgent.ts @@ -16,16 +16,30 @@ import { ai } from './genkit.js'; import { reportAbsence, reportTardy } from './tools.js'; +import { agentDescription } from './util.js'; + +const tools = [reportAbsence, reportTardy, 'routingAgent']; +const specialization = 'attendance'; + +const toolNames: string[] = tools.map((item) => { + if (typeof item === 'string') { + return item; + } else { + return item.name; + } +}); export const attendanceAgent = ai.definePrompt( { - name: 'attendanceAgent', - description: - 'transfer to this agent when the user asks questions about attendance-related concerns like tardies or absences. do not mention that you are transferring, just do it', - tools: [reportAbsence, reportTardy], + name: `${specialization}Agent`, + description: agentDescription(specialization, toolNames), + tools, }, ` {{ role "system"}} - You are Bell, a helpful attendance assistance agent for Sparkyville High School. A parent has been referred to you to handle an attendance-related concern. Use the tools available to you to assist the parent. + +You are Bell, a helpful attendance assistance agent for Sparkyville High School. +A parent has been referred to you to handle a ${specialization}-related concern. +Use the tools available to you to assist the parent. - Parents may only report absences for their own students. - If you are unclear about any of the fields required to report an absence or tardy, request clarification before using the tool. diff --git a/samples/js-schoolAgent/src/data.ts b/samples/js-schoolAgent/src/data.ts index 2c0ba130e..b44268d76 100644 --- a/samples/js-schoolAgent/src/data.ts +++ b/samples/js-schoolAgent/src/data.ts @@ -62,3 +62,35 @@ export const EXAMPLE_EVENTS = [ grades: [9, 10, 11, 12], }, ]; + +export interface GradeEntry { + studentId: number; + subject: string; + grade: string; + date: string; + assignment: string; +} + +export const EXAMPLE_GRADES: GradeEntry[] = [ + { + studentId: 3734, + subject: 'Mathematics', + grade: 'A-', + date: '2024-03-01', + assignment: 'Quadratic Equations Quiz', + }, + { + studentId: 3734, + subject: 'English', + grade: 'B+', + date: '2024-03-05', + assignment: 'Essay: Shakespeare Analysis', + }, + { + studentId: 9433, + subject: 'Physics', + grade: 'A', + date: '2024-03-02', + assignment: 'Forces Lab Report', + }, +]; diff --git a/samples/js-schoolAgent/src/gradesAgent.ts b/samples/js-schoolAgent/src/gradesAgent.ts new file mode 100644 index 000000000..58871fc52 --- /dev/null +++ b/samples/js-schoolAgent/src/gradesAgent.ts @@ -0,0 +1,53 @@ +/** + * Copyright 2024 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 { ai } from './genkit.js'; +import { getRecentGrades } from './tools.js'; +import { agentDescription } from './util.js'; + +const tools = [getRecentGrades, 'routingAgent']; +const specialization = 'grades'; + +const toolNames: string[] = tools.map((item) => { + if (typeof item === 'string') { + return item; + } else { + return item.name; + } +}); + +export const gradesAgent = ai.definePrompt( + { + name: `${specialization}Agent`, + description: agentDescription(specialization, toolNames), + tools, + }, + ` {{ role "system"}} + +You are Bell, a helpful attendance assistance agent for Sparkyville High School. +A parent has been referred to you to handle a ${specialization}-related concern. +Use the tools available to you to assist the parent. + +Guidelines: +- Parents may only view grades for their own students +- Always verify the student belongs to the parent before sharing grade information +- Be encouraging and positive when discussing grades +- If asked about non-grade related topics, transfer back to the info agent +- Format grade information in a clear, easy-to-read manner + +{{ userContext @state }} + ` +); diff --git a/samples/js-schoolAgent/src/infoAgent.ts b/samples/js-schoolAgent/src/infoAgent.ts deleted file mode 100644 index 6ed13ecc3..000000000 --- a/samples/js-schoolAgent/src/infoAgent.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2024 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 { attendanceAgent } from './attendanceAgent'; -import { ai } from './genkit'; -import { searchEvents, upcomingHolidays } from './tools.js'; - -export const infoAgent = ai.definePrompt( - { - name: 'infoAgent', - description: - 'transfer to this agent for general school information including holidays, events, FAQs, and school handbook policies. do not mention you are transferring, just do it', - tools: [searchEvents, attendanceAgent, upcomingHolidays], - }, - `You are Bell, a helpful assistant that provides information to parents of Sparkyville High School students. Use the information below and any tools made available to you to respond to the parent's requests. - -=== Frequently Asked Questions - -- Classes begin at 8am, students are dismissed at 3:30pm -- Parking permits are issued on a first-come first-serve basis beginning Aug 1 - -{{userContext @state }} -` -); diff --git a/samples/js-schoolAgent/src/routingAgent.ts b/samples/js-schoolAgent/src/routingAgent.ts new file mode 100644 index 000000000..3986baa4f --- /dev/null +++ b/samples/js-schoolAgent/src/routingAgent.ts @@ -0,0 +1,50 @@ +/** + * Copyright 2024 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 { attendanceAgent } from './attendanceAgent'; +import { ai } from './genkit'; +import { gradesAgent } from './gradesAgent'; +import { searchEvents, upcomingHolidays } from './tools.js'; + +export const routingAgent = ai.definePrompt( + { + name: 'routingAgent', + description: `This agent helps with answering inquiries and requests.`, + tools: [searchEvents, attendanceAgent, gradesAgent, upcomingHolidays], + }, + `You are Bell, the friendly AI office receptionist at Sparkyville High School. + + Your job is to help answer inquiries from parents. Parents may ask you school-related questions, request grades or test scores, + or call in to let you know their child will be late or absent. + + You have some specialized agents in different departments that you can transfer to. + + 1. Grades Agent - This agent can provide informtion about previous scores for assignments and tests. + 2. Attendance Agent - This agent can help with attendance requests, such as marking a student as late/tardy or absent. + + Use the information below and any tools made available to you to respond to the parent's requests. + + If the parent has an inquiry that you do not know the answer to, do NOT make the answer up. Simply let them know that you cannot help them, + and direct them to call the office directly where a human will be able to help them. + + === Frequently Asked Questions + + - Classes begin at 8am, students are dismissed at 3:30pm + - Parking permits are issued on a first-come first-serve basis beginning Aug 1 + + {{userContext @state }} +` +); diff --git a/samples/js-schoolAgent/src/terminal.ts b/samples/js-schoolAgent/src/terminal.ts index d0c96c754..9b3717449 100644 --- a/samples/js-schoolAgent/src/terminal.ts +++ b/samples/js-schoolAgent/src/terminal.ts @@ -14,9 +14,10 @@ * limitations under the License. */ +import { Message, ToolRequestPart } from 'genkit'; import { createInterface } from 'node:readline'; import { ai } from './genkit.js'; -import { infoAgent } from './infoAgent.js'; +import { routingAgent } from './routingAgent.js'; const rl = createInterface({ input: process.stdin, @@ -37,50 +38,85 @@ const EXAMPLE_USER_CONTEXT = { ], }; -async function main() { - const chat = ai - .createSession({ initialState: EXAMPLE_USER_CONTEXT }) - .chat(infoAgent); +// ANSI color codes for terminal output +const COLORS = { + BELL: '\x1b[33m', + PROMPT: '\x1b[36m', + RESET: '\x1b[0m', +}; + +// Helper to print colored text +function printColored(prefix: string, text: string, color: string) { + console.log(`${color}${prefix}>${COLORS.RESET}`, text); +} - const { text: greeting } = await ai.generate( +// Get initial greeting from AI +async function getGreeting() { + const { text } = await ai.generate( 'Come up with a short friendly greeting for yourself talking to a parent as Bell, the helpful AI assistant for parents of Sparkyville High School. Feel free to use emoji.' ); + return text; +} + +// Process and display the chat response stream +async function handleChatResponse( + stream: AsyncIterable<{ text: string }>, + response: Promise, + startMessageCount: number +) { console.log(); - console.log('\x1b[33mbell>\x1b[0m', greeting); - while (true) { - await new Promise((resolve) => { - rl.question('\n\x1b[36mprompt>\x1b[0m ', async (input) => { - try { - const start = chat.messages.length; - const { stream, response } = await chat.sendStream(input); - console.log(); - process.stdout.write('\x1b[33mbell>\x1b[0m '); - for await (const chunk of stream) { - process.stdout.write(chunk.text); - } - console.log( - '\nTools Used:', - (await response).messages - .slice(start) - .filter((m) => m.role === 'model') - .map((m) => - m.content - .filter((p) => !!p.toolRequest) - .map( - (p) => - `${p.toolRequest.name}(${JSON.stringify(p.toolRequest.input)})` - ) - ) - .flat() - .filter((t) => !!t) - ); + process.stdout.write(`${COLORS.BELL}bell>${COLORS.RESET} `); + + for await (const chunk of stream) { + process.stdout.write(chunk.text); + } + + // Extract and display tools used + const toolsUsed = (await response).messages + .slice(startMessageCount) + .filter((m: Message) => m.role === 'model') + .map((m: Message) => + m.content + .filter((p) => !!p.toolRequest) + .map( + (p) => + `${p.toolRequest?.name}(${JSON.stringify(p.toolRequest?.input)})` + ) + ) + .flat() + .filter((t: ToolRequestPart) => !!t); - resolve(null); - } catch (e) { - console.log('e', e); - } - }); + console.log('\nTools Used:', toolsUsed); +} + +// Main chat loop +async function handleUserInput(chat: any): Promise { + return new Promise((resolve) => { + rl.question(`\n${COLORS.PROMPT}prompt>${COLORS.RESET} `, async (input) => { + try { + const startMessageCount = chat.messages.length; + const { stream, response } = await chat.sendStream(input); + await handleChatResponse(stream, response, startMessageCount); + resolve(); + } catch (e) { + console.log('Error:', e); + resolve(); + } }); + }); +} + +async function main() { + const chat = ai + .createSession({ initialState: EXAMPLE_USER_CONTEXT }) + .chat(routingAgent); + + const greeting = await getGreeting(); + console.log(); + printColored('bell', greeting, COLORS.BELL); + + while (true) { + await handleUserInput(chat); } } diff --git a/samples/js-schoolAgent/src/tools.ts b/samples/js-schoolAgent/src/tools.ts index ee0efa741..b0e3b848b 100644 --- a/samples/js-schoolAgent/src/tools.ts +++ b/samples/js-schoolAgent/src/tools.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { EXAMPLE_EVENTS, getUpcomingHolidays } from './data.js'; +import { EXAMPLE_EVENTS, EXAMPLE_GRADES, getUpcomingHolidays } from './data.js'; import { ai, z } from './genkit.js'; import { AgentState } from './types.js'; @@ -46,11 +46,13 @@ export const searchEvents = ai.defineTool( ); function checkIsParent(studentId: number, state: AgentState) { - if (!state.students.find((s) => s.id === studentId)) { + const student = state.students.find((s) => s.id === studentId); + if (!student) { throw new Error( - "The provided student id did not match the parent's registered children." + `Unable to process request for student ID ${studentId}. Parents can only submit requests for their registered children.` ); } + return student; } export const reportAbsence = ai.defineTool( @@ -61,21 +63,21 @@ export const reportAbsence = ai.defineTool( inputSchema: z.object({ studentId: z.number().describe('the id of the student'), date: z.string().describe('the date of the absence in YYYY-MM-DD format'), - reason: z.string().describe('the provided reason reason for the absence'), + reason: z.string().describe('the provided reason for the absence'), excused: z .boolean() - .describe('whether the absense is excused by the parent'), + .describe('whether the absence is excused by the parent'), }), }, async (input) => { - checkIsParent(input.studentId, ai.currentSession().state!); - console.log( - '[TOOL] Student', + const student = checkIsParent( input.studentId, - 'has been reported absent for', - input.date + ai.currentSession().state! ); - return { ok: true }; + console.log( + `[TOOL] Absence reported for ${student.name} (ID: ${input.studentId}) on ${input.date}` + ); + return { ok: true, message: 'Absence successfully recorded' }; } ); @@ -118,3 +120,28 @@ export const upcomingHolidays = ai.defineTool( }, async () => JSON.stringify(await getUpcomingHolidays()) ); + +export const getRecentGrades = ai.defineTool( + { + name: 'getRecentGrades', + description: 'retrieves recent grades for a specific student', + inputSchema: z.object({ + studentId: z.number().describe('the id of the student'), + subject: z.string().optional().describe('optional subject filter'), + limit: z + .number() + .optional() + .describe('number of recent grades to return'), + }), + }, + async ({ studentId, subject, limit = 5 }) => { + checkIsParent(studentId, ai.currentSession().state!); + let grades = EXAMPLE_GRADES.filter((g) => g.studentId === studentId); + if (subject) { + grades = grades.filter( + (g) => g.subject.toLowerCase() === subject.toLowerCase() + ); + } + return grades.slice(0, limit); + } +); diff --git a/samples/js-schoolAgent/src/util.ts b/samples/js-schoolAgent/src/util.ts new file mode 100644 index 000000000..e66f628fe --- /dev/null +++ b/samples/js-schoolAgent/src/util.ts @@ -0,0 +1,20 @@ +/** + * Copyright 2024 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. + */ + +export const agentDescription = (specialization: string, tools: string[]) => ` +Transfer to this agent when the user asks about ${specialization}. +This agent can perform the following functions: ${tools.map((t) => t).join(', ')}. +Do not mention that you are transferring, just do it.`; From 4618930e09ec38e5d72d5f7cf54ca414ea2357be Mon Sep 17 00:00:00 2001 From: Peter Friese Date: Mon, 16 Dec 2024 21:17:04 -0800 Subject: [PATCH 138/562] docs: add codelab "Chat with a PDF file" (#1471) * Add "Chat with a PDF file" codelab * Add navigation section for codelabs * Fix UK spelling. Signed-off-by: Peter Friese * Fix typo. Signed-off-by: Peter Friese * Use bullet list for list of providers Signed-off-by: Peter Friese * Adjust line breaks to 80 colums Signed-off-by: Peter Friese * free -> free of charge Signed-off-by: Peter Friese * Comma Signed-off-by: Peter Friese * Typo Signed-off-by: Peter Friese * Use enumerate lists Signed-off-by: Peter Friese * Rrewrite for clarity Signed-off-by: Peter Friese * Re-write for clarity Signed-off-by: Peter Friese * Add extra line for clarity Signed-off-by: Peter Friese * Use enumerated list Signed-off-by: Peter Friese * More enumerated lists Signed-off-by: Peter Friese * Periods instead of colons Signed-off-by: Peter Friese --------- Signed-off-by: Peter Friese --- docs/_guides.yaml | 4 + docs/codelabs/codelab-chat-with-a-pdf.md | 201 +++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 docs/codelabs/codelab-chat-with-a-pdf.md diff --git a/docs/_guides.yaml b/docs/_guides.yaml index 94a203d1d..585a48e96 100644 --- a/docs/_guides.yaml +++ b/docs/_guides.yaml @@ -19,6 +19,10 @@ toc: path: /docs/genkit/get-started - title: Developer tools path: /docs/genkit/devtools + + - heading: Codelabs + - title: Chat with a PDF file + path: /docs/genkit/codelabs/codelab-chat-with-a-pdf - heading: Building AI workflows - title: Generating content diff --git a/docs/codelabs/codelab-chat-with-a-pdf.md b/docs/codelabs/codelab-chat-with-a-pdf.md new file mode 100644 index 000000000..f165fb915 --- /dev/null +++ b/docs/codelabs/codelab-chat-with-a-pdf.md @@ -0,0 +1,201 @@ +# Chat with a PDF file + +This codelab shows you how to use Genkit to implement an app that lets you +chat with a PDF file. + +## Prerequisites + +This codelab assumes that you’re familiar with building applications with +Node.js. To complete this codelab, make sure that your development environment +meets the following requirements: + +- Node.js v20+ +- npm + +## Create a new project + +1. Create a new empty folder. + + ```shell + mkdir chat-with-a-pdf + cd chat-with-a-pdf + ``` + +1. Initialize a new TypeScript project. + + ```shell + npm init -y + ``` + + +## Install Genkit + +Install the following Genkit dependencies to use Genkit in your project: + +- `genkit` provides Genkit core capabilities. +- `@genkit-ai/googleai` provides access to the Google AI Gemini models. + +```shell +npm install genkit @genkit-ai/googleai +``` + +## Configure your model API key + +For this guide, we’ll show you how to use the Gemini API, which provides a +generous free-of-charge tier and does not require a credit card to get +started. To use the Gemini API, you'll need an API key. If you don't +already have one, create a key in Google AI Studio. + +[Get an API key from Google AI Studio](https://makersuite.google.com/app/apikey) + +After you’ve created an API key, set the `GOOGLE_GENAI_API_KEY` environment +variable to your key with the following command: + +```shell +export GOOGLE_GENAI_API_KEY= +``` + +> **Note:** While this tutorial uses the Gemini API from AI Studio, Genkit +supports a wide variety of model providers, including: +> * [Gemini from Vertex AI](https://firebase.google.com/docs/genkit/plugins/vertex-ai#generative_ai_models) +> * Anthropic’s Claude 3 models and Llama 3.1 through the [Vertex AI Model Garden](https://firebase.google.com/docs/genkit/plugins/vertex-ai#anthropic_claude_3_on_vertex_ai_model_garden) +> * Open source models through [Ollama](https://firebase.google.com/docs/genkit/plugins/ollama) +> * [Community-supported providers](https://firebase.google.com/docs/genkit/models#models-supported) such as OpenAI and Cohere. + +## Import and initialise Genkit + +1. Create a new folder `src`, and inside it, a new file `index.ts`. Add the +following lines to import Genkit and the Google AI plugin. + + ```typescript + import {gemini15Flash, googleAI} from '@genkit-ai/googleai'; + import {genkit} from 'genkit'; + ``` + +1. Add the following lines to configure Genkit and set Gemini 1.5 Flash as the +default model. + + ```typescript + const ai = genkit({ + plugins: [googleAI()], + model: gemini15Flash, + }); + ``` + +1. Add the main body of your app. + + ```typescript + (async () => { + try { + // 1: get command line arguments + // 2: load PDF file + // 3: construct prompt + // 4: start chat + // 5: chat loop + } catch (error) { + console.error("Error parsing PDF or interacting with Genkit:", error); + } + })(); // <-- don't forget the trailing parentheses to call the function! + ``` + +## Load and parse a PDF file + +In this step, you will write code to load and parse a PDF file. + +1. Install `pdf-parse`. + + ```typescript + npm i pdf-parse + ``` + +1. Import the PDF library into your app. + + ```typescript + import pdf from 'pdf-parse'; + import fs from 'fs'; + ``` + +1. Read the PDF filename that was passed in from the command line. + + ```typescript + // 1: get command line arguments + const filename = process.argv[2]; + if (!filename) { + console.error("Please provide a filename as a command line argument."); + process.exit(1); + } + ``` + +1. Load the contents of the PDF file. + + ```typescript + // 2: load PDF file + let dataBuffer = fs.readFileSync(filename); + const { text } = await pdf(dataBuffer); + ``` + +## Set up the prompt + +Follow these steps to set up the prompt. + +1. Allow the user to provide a custom prompt via the command line. If they don’t +provide a prompt, use a default. + + ```typescript + const prefix = process.argv[3] || "Answer the user's questions about the contents of this PDF file."; + ``` + +1. Inject the prompt prefix and the full text of the PDF file into the prompt for +the model. + + ```typescript + const prompt = ` + ${prefix} + Context: + ${data.text} + ` + ``` + +## Implement the chat loop + +1. Start the chat with the model by calling the `chat` method, passing the prompt +(which includes the full text of the PDF file). + + ```typescript + const chat = ai.chat({ system: prompt }) + ``` + +1. Import `createInterface`; this will allow you to build a text-based UI. + + ```typescript + import {createInterface} from "node:readline/promises"; + ``` + +1. Instantiate a text input, then display a message to the user. + + ```typescript + const readline = createInterface(process.stdin, process.stdout); + console.log("You're chatting with Gemini. Ctrl-C to quit.\n"); + ``` + +1. Read the user’s input, then send it to the model using `chat.send`. This part +of the app will loop until the user presses _CTRL + C_. + + ```typescript + while (true) { + const userInput = await readline.question("> "); + const {text} = await chat.send(userInput); + console.log(text); + } + ``` + +## Run the app + +You can now run the app from your terminal. Open the terminal in the root +folder of your project, then run the following command: + +```typescript +npx tsx src/index.ts path/to/some.pdf +``` + +You can then start chatting with the PDF file. From 5b9a9ff957ae94435c93a9ab2b731b1fb2d241c7 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Tue, 17 Dec 2024 12:12:59 -0500 Subject: [PATCH 139/562] docs: jsdocs and typedoc cleanup (#1526) --- js/ai/src/embedder.ts | 38 +- js/ai/src/extract.ts | 3 + js/ai/src/model.ts | 121 +++++- js/ai/src/prompt.ts | 15 + js/ai/src/retriever.ts | 37 ++ js/ai/src/types.ts | 14 - js/ai/typedoc.json | 15 + js/core/src/action.ts | 2 + js/core/src/flow.ts | 4 + js/core/src/index.ts | 7 + js/core/src/logging.ts | 9 + js/core/src/reflection.ts | 2 + js/core/src/schema.ts | 33 ++ js/core/src/statusTypes.ts | 3 + js/core/src/tracing.ts | 7 +- js/core/src/tracing/exporter.ts | 3 + js/core/src/tracing/instrumentation.ts | 17 +- js/core/src/utils.ts | 9 +- js/core/typedoc.json | 12 + js/genkit/README.md | 62 ++- js/genkit/src/embedder.ts | 1 - js/genkit/src/genkit.ts | 2 + js/genkit/src/index.ts | 31 +- js/genkit/src/model.ts | 1 - js/genkit/src/tracing.ts | 6 - js/index.typedoc.md | 115 ++++++ js/package.json | 10 +- js/plugins/dev-local-vectorstore/README.md | 73 +++- js/plugins/dev-local-vectorstore/src/index.ts | 8 +- js/plugins/firebase/README.md | 29 +- js/plugins/firebase/src/index.ts | 6 + js/plugins/google-cloud/README.md | 28 ++ js/plugins/google-cloud/src/gcpLogger.ts | 1 + .../google-cloud/src/gcpOpenTelemetry.ts | 3 + js/plugins/googleai/README.md | 25 +- js/plugins/googleai/src/gemini.ts | 14 + js/plugins/googleai/src/index.ts | 12 +- js/plugins/ollama/README.md | 29 +- js/plugins/ollama/src/index.ts | 4 +- js/plugins/ollama/src/types.ts | 41 +- js/plugins/pinecone/README.md | 43 +- js/plugins/vertexai/README.md | 25 +- js/plugins/vertexai/src/gemini.ts | 116 +++++- js/plugins/vertexai/src/index.ts | 8 + js/pnpm-lock.yaml | 390 +++--------------- js/typedoc.json | 29 +- 46 files changed, 1055 insertions(+), 408 deletions(-) create mode 100644 js/ai/typedoc.json create mode 100644 js/core/typedoc.json create mode 100644 js/index.typedoc.md create mode 100644 js/plugins/google-cloud/README.md diff --git a/js/ai/src/embedder.ts b/js/ai/src/embedder.ts index 89b050253..0f95adb7b 100644 --- a/js/ai/src/embedder.ts +++ b/js/ai/src/embedder.ts @@ -18,32 +18,52 @@ import { Action, defineAction, z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; import { Document, DocumentData, DocumentDataSchema } from './document.js'; -export type EmbeddingBatch = { embedding: number[] }[]; +/** + * Embedding vector. + */ +export type Embedding = number[]; -export const EmbeddingSchema = z.array(z.number()); -export type Embedding = z.infer; +/** + * A batch (array) of embeddings. + */ +export type EmbeddingBatch = { embedding: Embedding }[]; +/** + * A function used for embedder definition, encapsulates embedder implementation. + */ export type EmbedderFn = ( input: Document[], embedderOpts?: z.infer ) => Promise; +/** + * Zod schema of an embed request. + */ const EmbedRequestSchema = z.object({ input: z.array(DocumentDataSchema), options: z.any().optional(), }); +/** + * Zod schema of an embed response. + */ const EmbedResponseSchema = z.object({ - embeddings: z.array(z.object({ embedding: EmbeddingSchema })), + embeddings: z.array(z.object({ embedding: z.array(z.number()) })), // TODO: stats, etc. }); type EmbedResponse = z.infer; +/** + * Embedder action -- a subtype of {@link Action} with input/output types for embedders. + */ export type EmbedderAction = Action & { __configSchema?: CustomOptions; }; +/** + * Options of an `embed` function. + */ export interface EmbedderParams< CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, > { @@ -105,6 +125,9 @@ export function defineEmbedder< return ewm; } +/** + * A union type representing all the types that can refer to an embedder. + */ export type EmbedderArgument< CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, > = string | EmbedderAction | EmbedderReference; @@ -215,6 +238,9 @@ export async function embedMany< return response.embeddings; } +/** + * Zod schema of embedder info object. + */ export const EmbedderInfoSchema = z.object({ /** Friendly label for this model (e.g. "Google AI - Gemini Pro") */ label: z.string().optional(), @@ -232,6 +258,10 @@ export const EmbedderInfoSchema = z.object({ }); export type EmbedderInfo = z.infer; +/** + * A reference object that can used to resolve an embedder instance. Include additional type information + * about the specific embedder, e.g. custom config options schema. + */ export interface EmbedderReference< CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, > { diff --git a/js/ai/src/extract.ts b/js/ai/src/extract.ts index c5d014711..537cff49c 100644 --- a/js/ai/src/extract.ts +++ b/js/ai/src/extract.ts @@ -17,6 +17,9 @@ import JSON5 from 'json5'; import { Allow, parse } from 'partial-json'; +/** + * Parses partially complete JSON string. + */ export function parsePartialJson(jsonString: string): T { return JSON5.parse(JSON.stringify(parse(jsonString, Allow.ALL))); } diff --git a/js/ai/src/model.ts b/js/ai/src/model.ts index a1693b8ec..71a067b46 100644 --- a/js/ai/src/model.ts +++ b/js/ai/src/model.ts @@ -44,12 +44,22 @@ const EmptyPartSchema = z.object({ custom: z.record(z.unknown()).optional(), }); +/** + * Zod schema for a text part. + */ export const TextPartSchema = EmptyPartSchema.extend({ /** The text of the message. */ text: z.string(), }); + +/** + * Text part. + */ export type TextPart = z.infer; +/** + * Zod schema of a media part. + */ export const MediaPartSchema = EmptyPartSchema.extend({ media: z.object({ /** The media content type. Inferred from data uri if not provided. */ @@ -58,8 +68,15 @@ export const MediaPartSchema = EmptyPartSchema.extend({ url: z.string(), }), }); + +/** + * Media part. + */ export type MediaPart = z.infer; +/** + * Zod schema of a tool request part. + */ export const ToolRequestPartSchema = EmptyPartSchema.extend({ /** A request for a tool to be executed, usually provided by a model. */ toolRequest: z.object({ @@ -71,8 +88,15 @@ export const ToolRequestPartSchema = EmptyPartSchema.extend({ input: z.unknown().optional(), }), }); + +/** + * Tool part. + */ export type ToolRequestPart = z.infer; +/** + * Zod schema of a tool response part. + */ export const ToolResponsePartSchema = EmptyPartSchema.extend({ /** A provided response to a tool call. */ toolResponse: z.object({ @@ -84,19 +108,39 @@ export const ToolResponsePartSchema = EmptyPartSchema.extend({ output: z.unknown().optional(), }), }); + +/** + * Tool response part. + */ export type ToolResponsePart = z.infer; +/** + * Zod schema of a data part. + */ export const DataPartSchema = EmptyPartSchema.extend({ data: z.unknown(), }); +/** + * Data part. + */ export type DataPart = z.infer; +/** + * Zod schema of a custom part. + */ export const CustomPartSchema = EmptyPartSchema.extend({ custom: z.record(z.any()), }); + +/** + * Custom part. + */ export type CustomPart = z.infer; +/** + * Zod schema of message part. + */ export const PartSchema = z.union([ TextPartSchema, MediaPartSchema, @@ -106,18 +150,38 @@ export const PartSchema = z.union([ CustomPartSchema, ]); +/** + * Message part. + */ export type Part = z.infer; +/** + * Zod schema of a message role. + */ export const RoleSchema = z.enum(['system', 'user', 'model', 'tool']); + +/** + * Message role. + */ export type Role = z.infer; +/** + * Zod schema of a message. + */ export const MessageSchema = z.object({ role: RoleSchema, content: z.array(PartSchema), metadata: z.record(z.unknown()).optional(), }); + +/** + * Model message data. + */ export type MessageData = z.infer; +/** + * Zod schema of model info metadata. + */ export const ModelInfoSchema = z.object({ /** Acceptable names for this model (e.g. different versions). */ versions: z.array(z.string()).optional(), @@ -153,8 +217,15 @@ export const ModelInfoSchema = z.object({ .enum(['featured', 'stable', 'unstable', 'legacy', 'deprecated']) .optional(), }); + +/** + * Model info metadata. + */ export type ModelInfo = z.infer; +/** + * Zod schema of a tool definition. + */ export const ToolDefinitionSchema = z.object({ name: z.string(), description: z.string(), @@ -171,8 +242,15 @@ export const ToolDefinitionSchema = z.object({ .describe('additional metadata for this tool definition') .optional(), }); + +/** + * Tool definition. + */ export type ToolDefinition = z.infer; +/** + * Zod schema of a common config object. + */ export const GenerationCommonConfigSchema = z.object({ /** A specific version of a model family, e.g. `gemini-1.0-pro-001` for the `gemini-1.0-pro` family. */ version: z.string().optional(), @@ -182,8 +260,15 @@ export const GenerationCommonConfigSchema = z.object({ topP: z.number().optional(), stopSequences: z.array(z.string()).optional(), }); + +/** + * Common config object. + */ export type GenerationCommonConfig = typeof GenerationCommonConfigSchema; +/** + * Zod schema of output config. + */ const OutputConfigSchema = z.object({ format: z.string().optional(), schema: z.record(z.any()).optional(), @@ -191,6 +276,10 @@ const OutputConfigSchema = z.object({ instructions: z.string().optional(), contentType: z.string().optional(), }); + +/** + * Output config. + */ export type OutputConfig = z.infer; /** ModelRequestSchema represents the parameters that are passed to a model when generating content. */ @@ -207,19 +296,31 @@ export interface ModelRequest< > extends z.infer { config?: z.infer; } - +/** + * Zod schema of a generate request. + */ export const GenerateRequestSchema = ModelRequestSchema.extend({ /** @deprecated All responses now return a single candidate. This will always be `undefined`. */ candidates: z.number().optional(), }); + +/** + * Generate request data. + */ export type GenerateRequestData = z.infer; +/** + * Generate request. + */ export interface GenerateRequest< CustomOptionsSchema extends z.ZodTypeAny = z.ZodTypeAny, > extends z.infer { config?: z.infer; } +/** + * Zod schema of usage info from a generate request. + */ export const GenerationUsageSchema = z.object({ inputTokens: z.number().optional(), outputTokens: z.number().optional(), @@ -234,6 +335,10 @@ export const GenerationUsageSchema = z.object({ outputAudioFiles: z.number().optional(), custom: z.record(z.number()).optional(), }); + +/** + * Usage info from a generate request. + */ export type GenerationUsage = z.infer; /** @deprecated All responses now return a single candidate. Only the first candidate will be used if supplied. */ @@ -257,6 +362,9 @@ export const CandidateErrorSchema = z.object({ /** @deprecated All responses now return a single candidate. Only the first candidate will be used if supplied. */ export type CandidateError = z.infer; +/** + * Zod schema of a model response. + */ export const ModelResponseSchema = z.object({ message: MessageSchema.optional(), finishReason: z.enum(['stop', 'length', 'blocked', 'other', 'unknown']), @@ -268,8 +376,15 @@ export const ModelResponseSchema = z.object({ raw: z.unknown(), request: GenerateRequestSchema.optional(), }); + +/** + * Model response data. + */ export type ModelResponseData = z.infer; +/** + * Zod schema of generaete response. + */ export const GenerateResponseSchema = ModelResponseSchema.extend({ /** @deprecated All responses now return a single candidate. Only the first candidate will be used if supplied. Return `message`, `finishReason`, and `finishMessage` instead. */ candidates: z.array(CandidateSchema).optional(), @@ -277,6 +392,10 @@ export const GenerateResponseSchema = ModelResponseSchema.extend({ .enum(['stop', 'length', 'blocked', 'other', 'unknown']) .optional(), }); + +/** + * Generate response data. + */ export type GenerateResponseData = z.infer; /** ModelResponseChunkSchema represents a chunk of content to stream to the client. */ diff --git a/js/ai/src/prompt.ts b/js/ai/src/prompt.ts index 647a23277..4570fa57c 100644 --- a/js/ai/src/prompt.ts +++ b/js/ai/src/prompt.ts @@ -30,11 +30,17 @@ import { } from './model.js'; import { ToolAction } from './tool.js'; +/** + * Prompt implementation function signature. + */ export type PromptFn< I extends z.ZodTypeAny = z.ZodTypeAny, CustomOptionsSchema extends z.ZodTypeAny = z.ZodTypeAny, > = (input: z.infer) => Promise>; +/** + * Prompt action. + */ export type PromptAction = Action< I, typeof GenerateRequestSchema, @@ -58,6 +64,9 @@ export interface PromptConfig { metadata?: Record; } +/** + * Checks whether provided object is a prompt. + */ export function isPrompt(arg: any): boolean { return ( typeof arg === 'function' && @@ -65,6 +74,9 @@ export function isPrompt(arg: any): boolean { ); } +/** + * Generate options of a prompt. + */ export type PromptGenerateOptions< O extends z.ZodTypeAny = z.ZodTypeAny, CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, @@ -190,6 +202,9 @@ export async function renderPrompt< } as GenerateOptions; } +/** + * Checks whether the provided object is an executable prompt. + */ export function isExecutablePrompt(obj: any): boolean { return ( !!(obj as ExecutablePrompt)?.render && diff --git a/js/ai/src/retriever.ts b/js/ai/src/retriever.ts index a97d58670..1a116055e 100644 --- a/js/ai/src/retriever.ts +++ b/js/ai/src/retriever.ts @@ -28,11 +28,17 @@ export { type TextPart, } from './document.js'; +/** + * Retriever implementation function signature. + */ export type RetrieverFn = ( query: Document, queryOpts: z.infer ) => Promise; +/** + * Indexer implementation function signature. + */ export type IndexerFn = ( docs: Array, indexerOpts: z.infer @@ -54,6 +60,9 @@ const IndexerRequestSchema = z.object({ options: z.any().optional(), }); +/** + * Zod schema of retriever info metadata. + */ export const RetrieverInfoSchema = z.object({ label: z.string().optional(), /** Supported model capabilities. */ @@ -66,11 +75,17 @@ export const RetrieverInfoSchema = z.object({ }); export type RetrieverInfo = z.infer; +/** + * A retriever action type. + */ export type RetrieverAction = Action & { __configSchema?: CustomOptions; }; +/** + * An indexer action type. + */ export type IndexerAction = Action & { __configSchema?: IndexerOptions; @@ -192,6 +207,9 @@ export interface RetrieverParams< options?: z.infer; } +/** + * A type that can be used to pass a retriever as an argument, either using a reference or an action. + */ export type RetrieverArgument< CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, > = RetrieverAction | RetrieverReference | string; @@ -227,9 +245,15 @@ export async function retrieve( return response.documents.map((d) => new Document(d)); } +/** + * A type that can be used to pass an indexer as an argument, either using a reference or an action. + */ export type IndexerArgument = IndexerReference | IndexerAction | string; +/** + * Options passed to the index function. + */ export interface IndexerParams< CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, > { @@ -262,10 +286,16 @@ export async function index( }); } +/** + * Zod schema of common retriever options. + */ export const CommonRetrieverOptionsSchema = z.object({ k: z.number().describe('Number of documents to retrieve').optional(), }); +/** + * A retriver reference object. + */ export interface RetrieverReference { name: string; configSchema?: CustomOptions; @@ -285,6 +315,10 @@ export function retrieverRef< // Reuse the same schema for both indexers and retrievers -- for now. export const IndexerInfoSchema = RetrieverInfoSchema; + +/** + * Indexer metadata. + */ export type IndexerInfo = z.infer; export interface IndexerReference { @@ -349,6 +383,9 @@ function itemToMetadata( }); } +/** + * Simple retriever options. + */ export interface SimpleRetrieverOptions< C extends z.ZodTypeAny = z.ZodTypeAny, R = any, diff --git a/js/ai/src/types.ts b/js/ai/src/types.ts index e492fa3a6..d1920f574 100644 --- a/js/ai/src/types.ts +++ b/js/ai/src/types.ts @@ -17,13 +17,6 @@ import { Action, z } from '@genkit-ai/core'; import { toJsonSchema } from '@genkit-ai/core/schema'; -export const ModelIdSchema = z.object({ - modelProvider: z.string().readonly(), - modelName: z.string().readonly(), -}); - -export type ModelId = z.infer; - export const LlmStatsSchema = z.object({ latencyMs: z.number().optional(), inputTokenCount: z.number().optional(), @@ -79,10 +72,3 @@ export function toToolWireFormat( }; }); } - -// Does it even make sense to have common options? since they are referenced differently in different LLMs. -export const CommonLlmOptions = z.object({ - temperature: z.number().optional(), - topK: z.number().optional(), - topP: z.number().optional(), -}); diff --git a/js/ai/typedoc.json b/js/ai/typedoc.json new file mode 100644 index 000000000..5ba96f739 --- /dev/null +++ b/js/ai/typedoc.json @@ -0,0 +1,15 @@ +{ + "entryPoints": [ + "src/index.ts", + "src/retriever.ts", + "src/embedder.ts", + "src/evaluator.ts", + "src/model.ts", + "src/model/middleware.ts", + "src/extract.ts", + "src/tool.ts", + "src/reranker.ts", + "src/chat.ts", + "src/session.ts" + ] +} diff --git a/js/core/src/action.ts b/js/core/src/action.ts index f5120e343..8c4cca4b5 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -399,6 +399,8 @@ export function runWithStreamingCallback( /** * Retrieves the {@link StreamingCallback} previously set by {@link runWithStreamingCallback} + * + * @hidden */ export function getStreamingCallback( registry: Registry diff --git a/js/core/src/flow.ts b/js/core/src/flow.ts index 72c99c64e..21b3f056a 100644 --- a/js/core/src/flow.ts +++ b/js/core/src/flow.ts @@ -47,6 +47,8 @@ export interface FlowAuthPolicy { /** * For express-based flows, req.auth should contain the value to bepassed into * the flow context. + * + * @hidden */ export interface __RequestWithAuth extends express.Request { auth?: unknown; @@ -400,6 +402,8 @@ export interface FlowServerOptions { * Flow server exposes registered flows as HTTP endpoints. * * This is for use in production environments. + * + * @hidden */ export class FlowServer { /** List of all running servers needed to be cleaned up on process exit. */ diff --git a/js/core/src/index.ts b/js/core/src/index.ts index 29e06d89f..7dce71659 100644 --- a/js/core/src/index.ts +++ b/js/core/src/index.ts @@ -16,7 +16,14 @@ import { version } from './__codegen/version.js'; +/** + * Genkit library version. + */ export const GENKIT_VERSION = version; + +/** + * Genkit client header for API calls. + */ export const GENKIT_CLIENT_HEADER = `genkit-node/${GENKIT_VERSION} gl-node/${process.versions.node}`; export const GENKIT_REFLECTION_API_SPEC_VERSION = 1; diff --git a/js/core/src/logging.ts b/js/core/src/logging.ts index c1d49f7e1..284fc5614 100644 --- a/js/core/src/logging.ts +++ b/js/core/src/logging.ts @@ -81,4 +81,13 @@ class Logger { } } +/** + * Genkit logger. + * + * ```ts + * import { logger } from 'genkit/logging'; + * + * logger.setLogLevel('debug'); + * ``` + */ export const logger = new Logger(); diff --git a/js/core/src/reflection.ts b/js/core/src/reflection.ts index 9ef844503..d8d2c4fba 100644 --- a/js/core/src/reflection.ts +++ b/js/core/src/reflection.ts @@ -52,6 +52,8 @@ export interface ReflectionServerOptions { * Reflection server exposes an API for inspecting and interacting with Genkit in development. * * This is for use in development environments. + * + * @hidden */ export class ReflectionServer { /** List of all running servers needed to be cleaned up on process exit. */ diff --git a/js/core/src/schema.ts b/js/core/src/schema.ts index a53da8acb..45f1a75ab 100644 --- a/js/core/src/schema.ts +++ b/js/core/src/schema.ts @@ -24,15 +24,26 @@ const ajv = new Ajv(); addFormats(ajv); export { z }; // provide a consistent zod to use throughout genkit + +/** + * JSON schema. + */ export type JSONSchema = JSONSchemaType | any; + const jsonSchemas = new WeakMap(); const validators = new WeakMap>(); +/** + * Wrapper object for various ways schema can be provided. + */ export interface ProvidedSchema { jsonSchema?: JSONSchema; schema?: z.ZodTypeAny; } +/** + * Schema validation error. + */ export class ValidationError extends GenkitError { constructor({ data, @@ -72,6 +83,9 @@ export function toJsonSchema({ return outSchema as JSONSchema; } +/** + * Schema validation error details. + */ export interface ValidationErrorDetail { path: string; message: string; @@ -84,10 +98,16 @@ function toErrorDetail(error: ErrorObject): ValidationErrorDetail { }; } +/** + * Validation response. + */ export type ValidationResponse = | { valid: true; errors: never } | { valid: false; errors: ErrorObject[] }; +/** + * Validates the provided data against the provided schema. + */ export function validateSchema( data: unknown, options: ProvidedSchema @@ -102,6 +122,9 @@ export function validateSchema( return { valid, errors: errors?.map(toErrorDetail), schema: toValidate }; } +/** + * Parses raw data object agaisnt the provided schema. + */ export function parseSchema( data: unknown, options: ProvidedSchema @@ -111,6 +134,11 @@ export function parseSchema( return data as T; } +/** + * Registers provided schema as a named schema object in the Genkit registry. + * + * @hidden + */ export function defineSchema( registry: Registry, name: string, @@ -120,6 +148,11 @@ export function defineSchema( return schema; } +/** + * Registers provided JSON schema as a named schema object in the Genkit registry. + * + * @hidden + */ export function defineJsonSchema( registry: Registry, name: string, diff --git a/js/core/src/statusTypes.ts b/js/core/src/statusTypes.ts index e6cbd5cce..171ffe0dd 100644 --- a/js/core/src/statusTypes.ts +++ b/js/core/src/statusTypes.ts @@ -16,6 +16,9 @@ import * as z from 'zod'; +/** + * Enumeration of response status codes. + */ export enum StatusCodes { // Not an error; returned on success. // diff --git a/js/core/src/tracing.ts b/js/core/src/tracing.ts index 5b981bbe1..85e096f1e 100644 --- a/js/core/src/tracing.ts +++ b/js/core/src/tracing.ts @@ -35,6 +35,9 @@ let nodeOtelConfig: TelemetryConfig | null = null; const instrumentationKey = '__GENKIT_TELEMETRY_INSTRUMENTED'; +/** + * @hidden + */ export async function ensureBasicTelemetryInstrumentation() { if (global[instrumentationKey]) { return await global[instrumentationKey]; @@ -115,7 +118,9 @@ function maybeFlushMetrics(): Promise { } /** - * Flushes all configured span processors + * Flushes all configured span processors. + * + * @hidden */ export async function flushTracing() { if (nodeOtelConfig?.spanProcessors) { diff --git a/js/core/src/tracing/exporter.ts b/js/core/src/tracing/exporter.ts index a267c5360..c9092317b 100644 --- a/js/core/src/tracing/exporter.ts +++ b/js/core/src/tracing/exporter.ts @@ -27,6 +27,9 @@ import { SpanData, TraceData } from './types.js'; export let telemetryServerUrl: string | undefined; +/** + * @hidden + */ export function setTelemetryServerUrl(url: string) { telemetryServerUrl = url; } diff --git a/js/core/src/tracing/instrumentation.ts b/js/core/src/tracing/instrumentation.ts index 397460c40..34612ee2f 100644 --- a/js/core/src/tracing/instrumentation.ts +++ b/js/core/src/tracing/instrumentation.ts @@ -30,12 +30,13 @@ export const spanMetadataAlsKey = 'core.tracing.instrumentation.span'; export const traceMetadataAlsKey = 'core.tracing.instrumentation.trace'; export const ATTR_PREFIX = 'genkit'; +/** @hidden */ export const SPAN_TYPE_ATTR = ATTR_PREFIX + ':type'; const TRACER_NAME = 'genkit-tracer'; const TRACER_VERSION = 'v1'; /** - * + * @hidden */ export async function newTrace( registry: Registry | HasRegistry, @@ -77,6 +78,8 @@ export async function newTrace( /** * Runs the provided function in a new span. + * + * @hidden */ export async function runInNewSpan( registry: Registry | HasRegistry, @@ -142,6 +145,8 @@ export async function runInNewSpan( /** * Creates a new child span and attaches it to a previously created trace. This * is useful, for example, for adding deferred user engagement metadata. + * + * @hidden */ export async function appendSpan( traceId: string, @@ -197,6 +202,8 @@ function metadataToAttributes(metadata: SpanMetadata): Record { /** * Sets provided attribute value in the current span. + * + * @hidden */ export function setCustomMetadataAttribute( registry: Registry, @@ -215,6 +222,8 @@ export function setCustomMetadataAttribute( /** * Sets provided attribute values in the current span. + * + * @hidden */ export function setCustomMetadataAttributes( registry: Registry, @@ -232,7 +241,11 @@ export function setCustomMetadataAttributes( } } -/** Converts a fully annotated path to a friendly display version for logs */ +/** + * Converts a fully annotated path to a friendly display version for logs + * + * @hidden + */ export function toDisplayPath(path: string): string { const pathPartRegex = /\{([^\,}]+),[^\}]+\}/g; return Array.from(path.matchAll(pathPartRegex), (m) => m[1]).join(' > '); diff --git a/js/core/src/utils.ts b/js/core/src/utils.ts index 6ea4708d4..7bbde519f 100644 --- a/js/core/src/utils.ts +++ b/js/core/src/utils.ts @@ -32,6 +32,8 @@ export function deleteUndefinedProps(obj: any) { /** * Returns the current environment that the app code is running in. + * + * @hidden */ export function getCurrentEnv(): string { return process.env.GENKIT_ENV || 'prod'; @@ -44,13 +46,6 @@ export function isDevEnv(): boolean { return getCurrentEnv() === 'dev'; } -/** - * Adds flow-specific prefix for OpenTelemetry span attributes. - */ -export function flowMetadataPrefix(name: string) { - return `flow:${name}`; -} - /** * Adds flow-specific prefix for OpenTelemetry span attributes. */ diff --git a/js/core/typedoc.json b/js/core/typedoc.json new file mode 100644 index 000000000..ea1ad0b8f --- /dev/null +++ b/js/core/typedoc.json @@ -0,0 +1,12 @@ +{ + "entryPoints": [ + "src/index.ts", + "src/metrics.ts", + "src/registry.ts", + "src/tracing.ts", + "src/logging.ts", + "src/config.ts", + "src/runtime.ts", + "src/schema.ts" + ] +} diff --git a/js/genkit/README.md b/js/genkit/README.md index 324aee45e..1a7d47f35 100644 --- a/js/genkit/README.md +++ b/js/genkit/README.md @@ -1,7 +1,65 @@ # Genkit -The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. +Genkit is a framework for building AI-powered applications. It provides open source libraries for Node.js and Go, along with tools to help you debug and iterate quickly. -Usage information and reference details can be found in [Genkit documentation](https://firebase.google.com/docs/genkit). +## Prerequisites + +This guide assumes that you're familiar with building applications with Node.js. + +To complete this quickstart, make sure that your development environment meets +the following requirements: + +- Node.js v20+ +- npm + +## Install Genkit dependencies + +Install the following Genkit dependencies to use Genkit in your project: + +- `genkit` provides Genkit core capabilities. +- `@genkit-ai/googleai` provides access to the Google AI Gemini models. Check out other plugins: https://www.npmjs.com/search?q=keywords:genkit-plugin + +```posix-terminal +npm install genkit @genkit-ai/googleai +``` + +## Make your first request + +Get started with Genkit in just a few lines of simple code. + +```ts +// import the Genkit and Google AI plugin libraries +import { gemini15Flash, googleAI } from '@genkit-ai/googleai'; +import { genkit } from 'genkit'; + +// configure a Genkit instance +const ai = genkit({ + plugins: [googleAI()], + model: gemini15Flash, // set default model +}); + +(async () => { + // make a generation request + const { text } = await ai.generate('Hello, Gemini!'); + console.log(text); +})(); +``` + +## Next steps + +Now that you’re set up to make model requests with Genkit, learn how to use more +Genkit capabilities to build your AI-powered apps and workflows. To get started +with additional Genkit capabilities, see the following guides: + +- [Developer tools](/docs/genkit/devtools): Learn how to set up and use + Genkit’s CLI and developer UI to help you locally test and debug your app. +- [Generating content](/docs/genkit/models): Learn how to use Genkit’s unified + generation API to generate text and structured data from any supported + model. +- [Creating flows](/docs/genkit/flows): Learn how to use special Genkit + functions, called flows, that provide end-to-end observability for workflows + and rich debugging from Genkit tooling. +- [Managing prompts](/docs/genkit/dotprompt): Learn how Genkit helps you manage + your prompts and configuration together as code. License: Apache 2.0 diff --git a/js/genkit/src/embedder.ts b/js/genkit/src/embedder.ts index 620a1e7db..ba4e5d399 100644 --- a/js/genkit/src/embedder.ts +++ b/js/genkit/src/embedder.ts @@ -16,7 +16,6 @@ export { EmbedderInfoSchema, - EmbeddingSchema, embedderRef, type EmbedderAction, type EmbedderArgument, diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index 0565ea0ff..1601e7539 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -166,6 +166,8 @@ export type PromptMetadata< /** * `Genkit` encapsulates a single Genkit instance including the {@link Registry}, {@link ReflectionServer}, {@link FlowServer}, and configuration. * + * Do not instantiate this class directly. Use {@link genkit}. + * * Registry keeps track of actions, flows, tools, and many other components. Reflection server exposes an API to inspect the registry and trigger executions of actions in the registry. Flow server exposes flows as HTTP endpoints for production use. * * There may be multiple Genkit instances in a single codebase. diff --git a/js/genkit/src/index.ts b/js/genkit/src/index.ts index 33b7eb36a..723dee1fb 100644 --- a/js/genkit/src/index.ts +++ b/js/genkit/src/index.ts @@ -1,4 +1,6 @@ /** + * @license + * * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,7 +16,17 @@ * limitations under the License. */ +/** + * Main genkit import. + * + * ```ts + * import { genkit } from 'genkit'; + * ``` + * + * @module / + */ export { + BaseDataPointSchema, Document, DocumentDataSchema, GenerationBlockedError, @@ -24,23 +36,17 @@ export { LlmStatsSchema, Message, MessageSchema, - ModelIdSchema, ModelRequestSchema, ModelResponseSchema, PartSchema, RoleSchema, ToolCallSchema, ToolSchema, - asTool, embedderRef, evaluatorRef, indexerRef, rerankerRef, retrieverRef, - toGenerateRequest, - toToolWireFormat, - type BaseDataPointSchema, - type CommonLlmOptions, type DocumentData, type EmbedderAction, type EmbedderArgument, @@ -71,7 +77,6 @@ export { type MediaPart, type MessageData, type ModelArgument, - type ModelId, type ModelReference, type ModelRequest, type ModelResponseData, @@ -106,15 +111,10 @@ export { GENKIT_VERSION, GenkitError, ReflectionServer, - RunActionResponseSchema, StatusCodes, StatusSchema, - defineFlow, defineJsonSchema, defineSchema, - defineStreamingFlow, - deleteUndefinedProps, - flowMetadataPrefix, getCurrentEnv, getFlowAuth, getStreamingCallback, @@ -143,4 +143,9 @@ export { type __RequestWithAuth, } from '@genkit-ai/core'; export { loadPromptFile } from '@genkit-ai/dotprompt'; -export * from './genkit.js'; +export { + Genkit, + genkit, + type GenkitOptions, + type PromptMetadata, +} from './genkit.js'; diff --git a/js/genkit/src/model.ts b/js/genkit/src/model.ts index 87a29f0fa..b662acd77 100644 --- a/js/genkit/src/model.ts +++ b/js/genkit/src/model.ts @@ -28,7 +28,6 @@ export { MessageSchema, ModelInfoSchema, ModelRequestSchema, - ModelResponseChunkSchema, ModelResponseSchema, PartSchema, RoleSchema, diff --git a/js/genkit/src/tracing.ts b/js/genkit/src/tracing.ts index 766e0d1ef..5ad9431a1 100644 --- a/js/genkit/src/tracing.ts +++ b/js/genkit/src/tracing.ts @@ -15,10 +15,6 @@ */ export { - GenkitSpanProcessorWrapper, - InstrumentationLibrarySchema, - LinkSchema, - PathMetadataSchema, SPAN_TYPE_ATTR, SpanContextSchema, SpanDataSchema, @@ -29,9 +25,7 @@ export { TraceMetadataSchema, TraceServerExporter, appendSpan, - cleanUpTracing, enableTelemetry, - ensureBasicTelemetryInstrumentation, flushTracing, newTrace, runInNewSpan, diff --git a/js/index.typedoc.md b/js/index.typedoc.md new file mode 100644 index 000000000..f6d43a6ff --- /dev/null +++ b/js/index.typedoc.md @@ -0,0 +1,115 @@ +Genkit is a framework for building AI-powered applications. It provides open source libraries for Node.js and Go, along with tools to help you debug and iterate quickly. + +Learn more in our documentation for [Node.js](https://firebase.google.com/docs/genkit) and [Go](https://firebase.google.com/docs/genkit-go/get-started-go). + +## What can you build with Genkit? + +Genkit is a versatile framework, which you can use to build many different types of AI applications. Common use cases include: + +- **Intelligent agents:** Create agents that understand user requests and perform tasks autonomously, such as personalized travel planning or itinerary generation. + + - Example: [Compass Travel Planning App](https://developers.google.com/solutions/compass) + +- **Data transformation:** Convert unstructured data, like natural language, into structured formats (e.g., objects, SQL queries, tables) for integration into your app or data pipeline. + + - Example: [Add Natural Language AI Data Filters with Genkit](https://medium.com/firebase-developers/how-to-add-natural-language-ai-data-filters-to-your-app-71d64a79624d) + +- **Retrieval-augmented generation:** Create apps that provide accurate and contextually relevant responses by grounding generation with your own data sources, such as chatbots or question answering systems. + - Example: [Build AI features powered by your data](https://firebase.google.com/codelabs/ai-genkit-rag#0) + +## Who should use Genkit? + +Genkit is built for developers seeking to add generative AI to their apps with Node.js or Go, and can run anywhere these runtimes are supported. It's designed around a plugin architecture that can work with any generative model API or vector database, with many integrations [already available](#plugin-ecosystem). + +While developed by the [Firebase](https://firebase.google.com) team, Genkit can be used independently of Firebase or Google Cloud services. + +## Get started + +- [Node.js quickstart](https://firebase.google.com/docs/genkit/get-started) +- [Next.js quickstart](https://firebase.google.com/docs/genkit/nextjs) +- [Go quickstart](https://firebase.google.com/docs/genkit-go/get-started-go) + +> [!NOTE] +> Genkit for Go is in alpha, so we only recommend it for prototyping. + +## Library key features + +- **Unified generation API:** Generate text, media, structured objects, and tool calls from any generative model using a single, adaptable API. + +- **Vector database support:** Add retrieval-augmented generation (RAG) to your apps with simple indexing and retrieval APIs that work across vector database providers. + +- **Enhanced prompt engineering:** Define rich prompt templates, model configurations, input/output schemas, and tools all within a single, runnable [.prompt](https://firebase.google.com/docs/genkit/dotprompt) file. + +- **AI workflows:** Organize your AI app logic into [Flows](https://firebase.google.com/docs/genkit/flows) - functions designed for observability, streaming, integration with Genkit devtools, and easy deployment as API endpoints. + +- **Built-in streaming:** Stream content from your Genkit API endpoints to your client app to create snappy user experiences. + +## Development tools + +Genkit provides a CLI and a local UI to streamline your AI development workflow. + +### CLI + +The Genkit CLI includes commands for running and evaluating your Genkit functions (flows) and collecting telemetry and logs. + +- **Install:** `npm i -g genkit` +- **Run a command, wrapped with telemetry, a interactive developer UI, etc:** `genkit start -- ` + +### Developer UI + +The Genkit developer UI is a local interface for testing, debugging, and iterating on your AI application. + +Key features: + +- **Run:** Execute and experiment with Genkit flows, prompts, queries, and more in dedicated playgrounds. +- **Inspect:** Analyze detailed traces of past executions, including step-by-step breakdowns of complex flows. +- **Evaluate:** Review the results of evaluations run against your flows, including performance metrics and links to relevant traces. + +## Plugin ecosystem + +Extend Genkit with plugins for specific AI models, vector databases, and platform integrations from providers like Google and OpenAI. + +- **Node.js plugins:** [Explore on npm](https://www.npmjs.com/search?q=keywords:genkit-plugin) +- **Go plugins:** [Explore on pkg.go.dev](https://pkg.go.dev/github.com/firebase/genkit/go#section-directories) + +Create and share your own plugins: + +- **Write Node.js plugins:** [Plugin Authoring Guide](https://firebase.google.com/docs/genkit/plugin-authoring) +- **Write Go plugins:** [Plugin Authoring Guide](https://firebase.google.com/docs/genkit-go/plugin-authoring) + +Find excellent examples of community-built plugins for OpenAI, Anthropic, Cohere, and more in this [repository](https://github.com/TheFireCo/genkit-plugins). + +## Try Genkit on IDX + +Want to skip the local setup? Click below to try out Genkit using [Project IDX](https://idx.dev), Google's AI-assisted workspace for full-stack app development in the cloud. + + + Try in IDX + + +## Sample apps + +Take a look at some samples of Genkit in use: + +- ["AI barista"](https://github.com/firebase/genkit/tree/main/samples/js-coffee-shop/) -- demonstrates simple LLM usage +- [A simple chatbot with a JavaScript frontend](https://github.com/firebase/genkit/tree/main/samples/chatbot/) -- add history to LLM sessions +- [Restaurant menu Q&A app](https://github.com/firebase/genkit/tree/main/samples/js-menu/) -- this sample shows progressively + more sophisticated versions of a menu understanding app. +- [Streaming to an Angular frontend](https://github.com/firebase/genkit/tree/main/samples/js-angular/) + +## Connect with us + +- **Join the community:** Stay updated, ask questions, and share your work with other Genkit users on our [Discord server](https://discord.gg/qXt5zzQKpc). + +- **Provide feedback:** Report issues or suggest new features using our GitHub [issue tracker](https://github.com/firebase/genkit/issues). + +## Contributing + +Contributions to Genkit are welcome and highly appreciated! See our [Contribution Guide](CONTRIBUTING.md) to get started. + +## Authors + +Genkit is built by [Firebase](https://firebase.google.com/products/genkit) with contributions from the [Open Source Community](https://github.com/firebase/genkit/graphs/contributors). diff --git a/js/package.json b/js/package.json index c85bab7cb..7925c4d62 100644 --- a/js/package.json +++ b/js/package.json @@ -18,16 +18,16 @@ "test:all": "pnpm -r --workspace-concurrency 0 -F \"./(ai|core|plugins|genkit)/**\" test && pnpm test:esm", "test:esm": "cd testapps/esm && pnpm test", "gendocs": "pnpm build && pnpm typedoc", - "typedoc-html": "typedoc --options typedoc.json" + "typedoc-html": "typedoc --sortEntryPoints false --options typedoc.json" }, "devDependencies": { "npm-run-all": "^4.1.5", "only-allow": "^1.2.1", "typescript": "^4.9.0", - "typedoc": "^0.26.11", - "typedoc-plugin-markdown": "^4.2.10", - "typedoc-plugin-zod": "^1.2.1", - "typedoc-material-theme": "^1.1.0", + "typedoc": "^0.27.5", + "typedoc-plugin-markdown": "^4.3.2", + "typedoc-plugin-zod": "^1.3.1", + "typedoc-github-theme": "^0.2.0", "tsx": "^4.19.2" }, "pnpm": { diff --git a/js/plugins/dev-local-vectorstore/README.md b/js/plugins/dev-local-vectorstore/README.md index 324aee45e..7dd9a1ade 100644 --- a/js/plugins/dev-local-vectorstore/README.md +++ b/js/plugins/dev-local-vectorstore/README.md @@ -1,4 +1,75 @@ -# Genkit +# Dev Local Vector Store for Genkit + +This is a simple implementation of a vector store that can be used to local development and testing. + +This plugin is not meant to be used in production. + +## Installing the plugin + +```bash +npm i --save @genkit-ai/dev-local-vectorstore +``` + +## Using the plugin + +```ts +import { Document, genkit } from 'genkit'; +import { + googleAI, + gemini15Flash, + textEmbeddingGecko001, +} from '@genkit-ai/googleai'; +import { + devLocalVectorstore, + devLocalIndexerRef, + devLocalRetrieverRef, +} from '@genkit-ai/dev-local-vectorstore'; + +const ai = genkit({ + plugins: [ + googleAI(), + devLocalVectorstore([ + { + indexName: 'BobFacts', + embedder: textEmbeddingGecko001, + }, + ]), + ], + model: gemini15Flash, +}); + +// Reference to a local vector database storing Genkit documentation +const indexer = devLocalIndexerRef('BobFacts'); +const retriever = devLocalRetrieverRef('BobFacts'); + +(async () => { + // Add documents to the index. Only do it once. + await ai.index({ + indexer: indexer, + documents: [ + Document.fromText('Bob lives on the moon.'), + Document.fromText('Bob is 42 years old.'), + Document.fromText('Bob likes bananas.'), + Document.fromText('Bob has 11 cats.'), + ], + }); + + const question = 'How old is Bob?'; + + // Consistent API to retrieve most relevant documents based on semantic similarity to query + const docs = await ai.retrieve({ + retriever: retriever, + query: question, + }); + + const result = await ai.generate({ + prompt: `Use the provided context from the Genkit documentation to answer this query: ${question}`, + docs, // Pass retrieved documents to the model + }); + + console.log(result.text); +})(); +``` The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. diff --git a/js/plugins/dev-local-vectorstore/src/index.ts b/js/plugins/dev-local-vectorstore/src/index.ts index 34859b916..1331c09e8 100644 --- a/js/plugins/dev-local-vectorstore/src/index.ts +++ b/js/plugins/dev-local-vectorstore/src/index.ts @@ -164,9 +164,7 @@ async function getClosestDocuments< /** * Configures a local vectorstore retriever */ -export function configureDevLocalRetriever< - EmbedderCustomOptions extends z.ZodTypeAny, ->( +function configureDevLocalRetriever( ai: Genkit, params: { indexName: string; @@ -203,9 +201,7 @@ export function configureDevLocalRetriever< /** * Configures a local vectorstore indexer. */ -export function configureDevLocalIndexer< - EmbedderCustomOptions extends z.ZodTypeAny, ->( +function configureDevLocalIndexer( ai: Genkit, params: { indexName: string; diff --git a/js/plugins/firebase/README.md b/js/plugins/firebase/README.md index 91662adcf..7f4e442fb 100644 --- a/js/plugins/firebase/README.md +++ b/js/plugins/firebase/README.md @@ -1,13 +1,34 @@ -# Genkit +# Firebase plugin for Genkit -The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. +## Installing the plugin -Usage information and reference details can be found in [Genkit documentation](https://firebase.google.com/docs/genkit). +```bash +npm i --save @genkit-ai/firebase +``` -License: Apache 2.0 +## Using the plugin + +```ts +import { genkit } from 'genkit'; +import { enableFirebaseTelemetry } from '@genkit-ai/firebase'; + +enableFirebaseTelemetry(); + +const ai = genkit({ + plugins: [ + // ... + ], +}); +``` ## Troubleshooting ### Telemetry upload reliability in Firebase Functions / Cloud Run When Genkit is hosted in Google Cloud Run (including Firebase Functions), telemetry data upload may be less reliable as the container switches to the "idle" [lifecycle state](https://cloud.google.com/blog/topics/developers-practitioners/lifecycle-container-cloud-run). If higher reliability is important to you, consider changing [CPU allocation](https://cloud.google.com/run/docs/configuring/cpu-allocation) to "always allocated" in the Google Cloud Console. Note that this impacts pricing. + +The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. + +Usage information and reference details can be found in [Genkit documentation](https://firebase.google.com/docs/genkit). + +License: Apache 2.0 diff --git a/js/plugins/firebase/src/index.ts b/js/plugins/firebase/src/index.ts index ce95e8beb..889783eba 100644 --- a/js/plugins/firebase/src/index.ts +++ b/js/plugins/firebase/src/index.ts @@ -1,4 +1,6 @@ /** + * @license + * * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,6 +16,10 @@ * limitations under the License. */ +/** + * @module / + */ + import { enableGoogleCloudTelemetry, GcpTelemetryConfigOptions, diff --git a/js/plugins/google-cloud/README.md b/js/plugins/google-cloud/README.md new file mode 100644 index 000000000..e8b4c25f4 --- /dev/null +++ b/js/plugins/google-cloud/README.md @@ -0,0 +1,28 @@ +# Google Cloud plugin for Genkit + +## Installing the plugin + +```bash +npm i --save @genkit-ai/google-cloud +``` + +## Using the plugin + +```ts +import { genkit } from 'genkit'; +import { enableGoogleCloudTelemetry } from '@genkit-ai/google-cloud'; + +enableGoogleCloudTelemetry(); + +const ai = genkit({ + plugins: [ + // ... + ], +}); +``` + +The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. + +Usage information and reference details can be found in [Genkit documentation](https://firebase.google.com/docs/genkit). + +License: Apache 2.0 diff --git a/js/plugins/google-cloud/src/gcpLogger.ts b/js/plugins/google-cloud/src/gcpLogger.ts index 496156eb5..78989883d 100644 --- a/js/plugins/google-cloud/src/gcpLogger.ts +++ b/js/plugins/google-cloud/src/gcpLogger.ts @@ -110,6 +110,7 @@ export class GcpLogger { } } +/** @hidden */ export function __addTransportStreamForTesting(stream: Writable) { additionalStream = stream; } diff --git a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts index c004e1b8d..0bf79c69d 100644 --- a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts +++ b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts @@ -462,14 +462,17 @@ function getErrorHandler( }; } +/** @hidden */ export function __getMetricExporterForTesting(): InMemoryMetricExporter { return metricExporter as InMemoryMetricExporter; } +/** @hidden */ export function __getSpanExporterForTesting(): InMemorySpanExporter { return spanExporter.getExporter() as InMemorySpanExporter; } +/** @hidden */ export function __forceFlushSpansForTesting() { spanProcessor.forceFlush(); } diff --git a/js/plugins/googleai/README.md b/js/plugins/googleai/README.md index 324aee45e..da7054d03 100644 --- a/js/plugins/googleai/README.md +++ b/js/plugins/googleai/README.md @@ -1,4 +1,27 @@ -# Genkit +# Google Gemini Developer API plugin for Genkit + +## Installing the plugin + +```bash +npm i --save @genkit-ai/googleai +``` + +## Using the plugin + +```ts +import { genkit } from 'genkit'; +import { googleAI, gemini } from '@genkit-ai/googleai'; + +const ai = genkit({ + plugins: [googleAI()], + model: gemini('gemini-1.5-flash'), +}); + +async () => { + const { text } = ai.generate('hi Gemini!'); + console.log(text); +}; +``` The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. diff --git a/js/plugins/googleai/src/gemini.ts b/js/plugins/googleai/src/gemini.ts index ede6dcb95..ebd65adfe 100644 --- a/js/plugins/googleai/src/gemini.ts +++ b/js/plugins/googleai/src/gemini.ts @@ -218,10 +218,24 @@ function longestMatchingPrefix(version: string, potentialMatches: string[]) { '' ); } + +/** + * Known model names, to allow code completion for convenience. Allows other model names. + */ export type GeminiVersionString = | keyof typeof SUPPORTED_GEMINI_MODELS | (string & {}); +/** + * Returns a reference to a model that can be used in generate calls. + * + * ```js + * await ai.generate({ + * prompt: 'hi', + * model: gemini('gemini-1.5-flash') + * }); + * ``` + */ export function gemini( version: GeminiVersionString, options: GeminiConfig = {} diff --git a/js/plugins/googleai/src/index.ts b/js/plugins/googleai/src/index.ts index 47bb6ae60..cf14d1030 100644 --- a/js/plugins/googleai/src/index.ts +++ b/js/plugins/googleai/src/index.ts @@ -33,6 +33,8 @@ import { gemini15Flash8b, gemini15Pro, gemini20FlashExp, + type GeminiConfig, + type GeminiVersionString, } from './gemini.js'; export { gemini, @@ -43,15 +45,23 @@ export { gemini20FlashExp, textEmbedding004, textEmbeddingGecko001, + type GeminiConfig, + type GeminiVersionString, }; export interface PluginOptions { apiKey?: string; apiVersion?: string | string[]; baseUrl?: string; - models?: (ModelReference | string)[]; + models?: ( + | ModelReference + | string + )[]; } +/** + * Google Gemini Developer API plugin. + */ export function googleAI(options?: PluginOptions): GenkitPlugin { return genkitPlugin('googleai', async (ai: Genkit) => { let apiVersions = ['v1']; diff --git a/js/plugins/ollama/README.md b/js/plugins/ollama/README.md index 324aee45e..2b173ef99 100644 --- a/js/plugins/ollama/README.md +++ b/js/plugins/ollama/README.md @@ -1,4 +1,31 @@ -# Genkit +# Ollama plugin for Genkit + +## Installing the plugin + +```bash +npm i --save genkitx-ollama +``` + +## Using the plugin + +```ts +import { genkit } from 'genkit'; +import { ollama } from 'genkitx-ollama'; + +const ai = genkit({ + plugins: [ + ollama({ + models: [{ name: 'gemma' }], + serverAddress: 'http://127.0.0.1:11434', // default local address + }), + ], +}); + +(async () => { + const { text } = ai.generate({prompt: 'hi Gemini!', model: 'ollama/gemma'); + console.log(text); +}); +``` The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. diff --git a/js/plugins/ollama/src/index.ts b/js/plugins/ollama/src/index.ts index 1a934a700..585712d8f 100644 --- a/js/plugins/ollama/src/index.ts +++ b/js/plugins/ollama/src/index.ts @@ -28,11 +28,11 @@ import { defineOllamaEmbedder } from './embeddings.js'; import { ApiType, ModelDefinition, - OllamaPluginParams, RequestHeaders, + type OllamaPluginParams, } from './types.js'; -export { defineOllamaEmbedder }; +export { type OllamaPluginParams }; export function ollama(params: OllamaPluginParams): GenkitPlugin { return genkitPlugin('ollama', async (ai: Genkit) => { diff --git a/js/plugins/ollama/src/types.ts b/js/plugins/ollama/src/types.ts index 0a3cd002f..8148b3a0c 100644 --- a/js/plugins/ollama/src/types.ts +++ b/js/plugins/ollama/src/types.ts @@ -46,15 +46,39 @@ export interface DefineOllamaEmbeddingParams { options: OllamaPluginParams; } -// Parameters for the Ollama plugin configuration +/** + * Parameters for the Ollama plugin configuration. + */ export interface OllamaPluginParams { /** * Array of models to be defined. + * + * ```ts + * const ai = genkit({ + * plugins: [ + * ollama({ + * models: [{ name: 'gemma' }], + * serverAddress: 'http://127.0.0.1:11434', // default local address + * }), + * ], + * }); + * ``` */ models?: ModelDefinition[]; /** * Array of embedding models to be defined. + * + * ```ts + * const ai = genkit({ + * plugins: [ + * ollama({ + * serverAddress: 'http://localhost:11434', + * embedders: [{ name: 'nomic-embed-text', dimensions: 768 }], + * }), + * ], + * }); + * ``` */ embedders?: EmbeddingModelDefinition[]; @@ -65,6 +89,21 @@ export interface OllamaPluginParams { /** * Optional request headers, which can be either static or dynamically generated. + * + * ```ts + * const ai = genkit({ + * plugins: [ + * ollama({ + * models: [...], + * serverAddress: 'https://my-deployment.server.address', + * requestHeaders: async (params) => { + * const headers = await fetchAuthHeaders(params.serverAddress); + * return { Authorization: headers['Authorization'] }; + * }, + * }), + * ], + * }); + * ``` */ requestHeaders?: RequestHeaders; } diff --git a/js/plugins/pinecone/README.md b/js/plugins/pinecone/README.md index 324aee45e..e238834b5 100644 --- a/js/plugins/pinecone/README.md +++ b/js/plugins/pinecone/README.md @@ -1,4 +1,45 @@ -# Genkit +# Pinecone plugin for Genkit + +## Installing the plugin + +```bash +npm i --save genkitx-pinecone +``` + +## Using the plugin + +```ts +import { genkit } from 'genkit'; +import { + pinecone, + pineconeRetrieverRef, + pineconeIndexerRef, +} from 'genkitx-pinecone'; + +const ai = genkit({ + plugins: [ + pinecone([ + { + indexId: 'bob-facts', + embedder: textEmbedding004, + }, + ]), + ], +}); + +export const bobFactsIndexer = pineconeIndexerRef({ + indexId: 'bob-facts', +}); +await ai.index({ indexer: bobFactsIndexer, documents }); + +// To specify an index: +export const bobFactsRetriever = pineconeRetrieverRef({ + indexId: 'bob-facts', +}); + +// To use the index you configured when you loaded the plugin: +let docs = await ai.retrieve({ retriever: pineconeRetrieverRef, query }); +``` The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. diff --git a/js/plugins/vertexai/README.md b/js/plugins/vertexai/README.md index 324aee45e..05a08ab01 100644 --- a/js/plugins/vertexai/README.md +++ b/js/plugins/vertexai/README.md @@ -1,4 +1,27 @@ -# Genkit +# A Vertex AI plugin for Genkit + +## Installing the plugin + +```bash +npm i --save @genkit-ai/vertexai +``` + +## Using the plugin + +```ts +import { genkit } from 'genkit'; +import { vertexAI, gemini, gemini15Flash } from '@genkit-ai/vertexai'; + +const ai = genkit({ + plugins: [vertexAI()], + model: gemini15Flash, +}); + +async () => { + const { text } = ai.generate('hi Gemini!'); + console.log(text); +}; +``` The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. diff --git a/js/plugins/vertexai/src/gemini.ts b/js/plugins/vertexai/src/gemini.ts index b6208c248..581001619 100644 --- a/js/plugins/vertexai/src/gemini.ts +++ b/js/plugins/vertexai/src/gemini.ts @@ -73,11 +73,91 @@ const GoogleSearchRetrievalSchema = z.object({ disableAttribution: z.boolean().optional(), }); +/** + * Zod schema of Gemini model options. + */ export const GeminiConfigSchema = GenerationCommonConfigSchema.extend({ + /** + * GCP region (e.g. us-central1) + */ + location: z.string().describe('banana').optional(), + + /** + * Safety filter settings. See: https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/configure-safety-filters#configurable-filters + * + * E.g. + * + * ```js + * config: { + * safetySettings: [ + * { + * category: 'HARM_CATEGORY_HATE_SPEECH', + * threshold: 'BLOCK_LOW_AND_ABOVE', + * }, + * { + * category: 'HARM_CATEGORY_DANGEROUS_CONTENT', + * threshold: 'BLOCK_MEDIUM_AND_ABOVE', + * }, + * { + * category: 'HARM_CATEGORY_HARASSMENT', + * threshold: 'BLOCK_ONLY_HIGH', + * }, + * { + * category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', + * threshold: 'BLOCK_NONE', + * }, + * ], + * } + * ``` + */ safetySettings: z.array(SafetySettingsSchema).optional(), - location: z.string().optional(), + + /** + * Vertex retrieval options. + * + * E.g. + * + * ```js + * config: { + * vertexRetrieval: { + * datastore: { + * projectId: 'your-cloud-project', + * location: 'us-central1', + * collection: 'your-collection', + * }, + * disableAttribution: true, + * } + * } + * ``` + */ vertexRetrieval: VertexRetrievalSchema.optional(), + + /** + * Google Search retrieval options. + * + * ```js + * config: { + * googleSearchRetrieval: { + * disableAttribution: true, + * } + * } + * ``` + */ googleSearchRetrieval: GoogleSearchRetrievalSchema.optional(), + + /** + * Function calling options. + * + * E.g. forced tool call: + * + * ```js + * config: { + * functionCallingConfig: { + * mode: 'ANY', + * } + * } + * ``` + */ functionCallingConfig: z .object({ mode: z.enum(['MODE_UNSPECIFIED', 'AUTO', 'ANY', 'NONE']).optional(), @@ -86,6 +166,40 @@ export const GeminiConfigSchema = GenerationCommonConfigSchema.extend({ .optional(), }); +/** + * Gemini model configuration options. + * + * E.g. + * ```js + * config: { + * temperature: 0.9, + * maxOutputTokens: 300, + * safetySettings: [ + * { + * category: 'HARM_CATEGORY_HATE_SPEECH', + * threshold: 'BLOCK_LOW_AND_ABOVE', + * }, + * { + * category: 'HARM_CATEGORY_DANGEROUS_CONTENT', + * threshold: 'BLOCK_MEDIUM_AND_ABOVE', + * }, + * { + * category: 'HARM_CATEGORY_HARASSMENT', + * threshold: 'BLOCK_ONLY_HIGH', + * }, + * { + * category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', + * threshold: 'BLOCK_NONE', + * }, + * ], + * functionCallingConfig: { + * mode: 'ANY', + * } + * } + * ``` + */ +export type GeminiConfig = z.infer; + export const gemini10Pro = modelRef({ name: 'vertexai/gemini-1.0-pro', info: { diff --git a/js/plugins/vertexai/src/index.ts b/js/plugins/vertexai/src/index.ts index a53bdb6ba..18114488a 100644 --- a/js/plugins/vertexai/src/index.ts +++ b/js/plugins/vertexai/src/index.ts @@ -1,4 +1,6 @@ /** + * @license + * * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,6 +16,10 @@ * limitations under the License. */ +/** + * @module / + */ + import { Genkit } from 'genkit'; import { GenkitPlugin, genkitPlugin } from 'genkit/plugin'; import { getDerivedParams } from './common/index.js'; @@ -34,6 +40,7 @@ import { gemini15Flash, gemini15Pro, gemini20FlashExp, + type GeminiConfig, } from './gemini.js'; import { SUPPORTED_IMAGEN_MODELS, @@ -56,6 +63,7 @@ export { textEmbeddingGecko003, textEmbeddingGeckoMultilingual001, textMultilingualEmbedding002, + type GeminiConfig, }; /** diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index f2adc7290..6ee43c330 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -21,17 +21,17 @@ importers: specifier: ^4.19.2 version: 4.19.2 typedoc: - specifier: ^0.26.11 - version: 0.26.11(typescript@4.9.5) - typedoc-material-theme: - specifier: ^1.1.0 - version: 1.1.0(typedoc@0.26.11(typescript@4.9.5)) + specifier: ^0.27.5 + version: 0.27.5(typescript@4.9.5) + typedoc-github-theme: + specifier: ^0.2.0 + version: 0.2.0(typedoc@0.27.5(typescript@4.9.5)) typedoc-plugin-markdown: - specifier: ^4.2.10 - version: 4.2.10(typedoc@0.26.11(typescript@4.9.5)) + specifier: ^4.3.2 + version: 4.3.2(typedoc@0.27.5(typescript@4.9.5)) typedoc-plugin-zod: - specifier: ^1.2.1 - version: 1.2.1(typedoc@0.26.11(typescript@4.9.5)) + specifier: ^1.3.1 + version: 1.3.1(typedoc@0.27.5(typescript@4.9.5)) typescript: specifier: ^4.9.0 version: 4.9.5 @@ -1344,7 +1344,7 @@ importers: version: link:../../plugins/ollama genkitx-openai: specifier: ^0.10.1 - version: 0.10.1(@genkit-ai/ai@1.0.0-dev.0)(@genkit-ai/core@1.0.0-dev.0) + version: 0.10.1(@genkit-ai/ai@1.0.0-rc.0)(@genkit-ai/core@1.0.0-rc.0) devDependencies: rimraf: specifier: ^6.0.1 @@ -2161,11 +2161,14 @@ packages: '@firebase/util@1.9.5': resolution: {integrity: sha512-PP4pAFISDxsf70l3pEy34Mf3GkkUcVQ3MdKp6aSVb7tcpfUQxnsdV7twDd8EkfB6zZylH6wpUAoangQDmCUMqw==} - '@genkit-ai/ai@1.0.0-dev.0': - resolution: {integrity: sha512-dtQyym12Z/yPw04h7n8n7amU0dpO8iB+uTyDTpSaRe7lkf3SUkNhWM+DDoKUZ70A6cqpAvXN0/haqD9ZpSN+FA==} + '@genkit-ai/ai@1.0.0-rc.0': + resolution: {integrity: sha512-755VyQwYXonxT9l6WShYuHPjOvhAvA0nf5hETTQtqrrE93hsG1QWVPC7tQ3lVrKNu/M4xmwGVsNgrRx3LIMYZg==} + + '@genkit-ai/core@1.0.0-rc.0': + resolution: {integrity: sha512-Sc/Ivlx2djH6716dfFXlBMpX6LCQRW/4rAg/AsG09Ux0Sygx8oj/GwHtRTNdzR+lNf907m21hpwxT8kLxd3a2w==} - '@genkit-ai/core@1.0.0-dev.0': - resolution: {integrity: sha512-mHSAdziskC7YOXkGXCK8299RmGu111dPnxzW2hy8epRpyTfis3SXkB1iJEZE3lxA9tKV9vG2zDxDI0bk4iQbXg==} + '@gerrit0/mini-shiki@1.24.4': + resolution: {integrity: sha512-YEHW1QeAg6UmxEmswiQbOVEg1CW22b1XUD/lNTliOsu0LD0wqoyleFMnmbTp697QE0pcadQiR5cVtbbAPncvpw==} '@google-cloud/aiplatform@3.25.0': resolution: {integrity: sha512-qKnJgbyCENjed8e1G5zZGFTxxNKhhaKQN414W2KIVHrLxMFmlMuG+3QkXPOWwXBnT5zZ7aMxypt5og0jCirpHg==} @@ -2672,9 +2675,6 @@ packages: resolution: {integrity: sha512-3hPesWomnmVeYMppEGYbyv0v/sRUugUdlFBNn9m1ueJYHAIKbvCErkWxNUH3guyKKYgJVrkvZoQxcd9faucSaw==} engines: {node: '>=18'} - '@material/material-color-utilities@0.2.7': - resolution: {integrity: sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==} - '@mistralai/mistralai-gcp@1.3.5': resolution: {integrity: sha512-eykxLojLv0AcgGui2D+D/S98Toc18j9g0GCvjyah3E8YtQW0dMb6UyQjmB+2+qDXN3OZjp8+dOkoJ+r7DmwbOQ==} peerDependencies: @@ -3251,20 +3251,14 @@ packages: cpu: [x64] os: [win32] - '@shikijs/core@1.23.1': - resolution: {integrity: sha512-NuOVgwcHgVC6jBVH5V7iblziw6iQbWWHrj5IlZI3Fqu2yx9awH7OIQkXIcsHsUmY19ckwSgUMgrqExEyP5A0TA==} - - '@shikijs/engine-javascript@1.23.1': - resolution: {integrity: sha512-i/LdEwT5k3FVu07SiApRFwRcSJs5QM9+tod5vYCPig1Ywi8GR30zcujbxGQFJHwYD7A5BUqagi8o5KS+LEVgBg==} + '@shikijs/engine-oniguruma@1.24.2': + resolution: {integrity: sha512-ZN6k//aDNWRJs1uKB12pturKHh7GejKugowOFGAuG7TxDRLod1Bd5JhpOikOiFqPmKjKEPtEA6mRCf7q3ulDyQ==} - '@shikijs/engine-oniguruma@1.23.1': - resolution: {integrity: sha512-KQ+lgeJJ5m2ISbUZudLR1qHeH3MnSs2mjFg7bnencgs5jDVPeJ2NVDJ3N5ZHbcTsOIh0qIueyAJnwg7lg7kwXQ==} + '@shikijs/types@1.24.2': + resolution: {integrity: sha512-bdeWZiDtajGLG9BudI0AHet0b6e7FbR0EsE4jpGaI0YwHm/XJunI9+3uZnzFtX65gsyJ6ngCIWUfA4NWRPnBkQ==} - '@shikijs/types@1.23.1': - resolution: {integrity: sha512-98A5hGyEhzzAgQh2dAeHKrWW4HfCMeoFER2z16p5eJ+vmPeF6lZ/elEne6/UCU551F/WqkopqRsr1l2Yu6+A0g==} - - '@shikijs/vscode-textmate@9.3.0': - resolution: {integrity: sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==} + '@shikijs/vscode-textmate@9.3.1': + resolution: {integrity: sha512-79QfK1393x9Ho60QFyLti+QfdJzRQCVLFb97kOIV7Eo9vQU/roINgk7m24uv0a7AUvN//RDH36FLjjK48v0s9g==} '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -3357,9 +3351,6 @@ packages: '@types/long@4.0.2': resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - '@types/memcached@2.2.10': resolution: {integrity: sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==} @@ -3450,9 +3441,6 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@ungap/structured-clone@1.2.0': - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -3688,9 +3676,6 @@ packages: resolution: {integrity: sha512-esx4bYDznnqgRX4G8kaEaf0W3q8xIc51WpmrIitDzmcoEgwnv9wSKdzT6UxWZ4wkVu5+ileofppX0TpyviJRdQ==} engines: {node: ^18.12.0 || >= 20.9.0} - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -3703,12 +3688,6 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} - character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} - - character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} - charenc@0.0.2: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} @@ -3796,9 +3775,6 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - commander@10.0.1: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} @@ -3962,10 +3938,6 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -3978,9 +3950,6 @@ packages: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} - devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4024,9 +3993,6 @@ packages: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} - emoji-regex-xs@1.0.0: - resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} - emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -4477,21 +4443,12 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hast-util-to-html@9.0.3: - resolution: {integrity: sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==} - - hast-util-whitespace@3.0.0: - resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -5205,9 +5162,6 @@ packages: md5@2.3.0: resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} - mdast-util-to-hast@13.2.0: - resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} - mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} @@ -5229,21 +5183,6 @@ packages: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} - micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - - micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} - - micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - - micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - - micromark-util-types@2.0.1: - resolution: {integrity: sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==} - micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -5449,9 +5388,6 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - oniguruma-to-es@0.4.1: - resolution: {integrity: sha512-rNcEohFz095QKGRovP/yqPIKc+nP+Sjs4YTHMv33nMePGKrq/r2eu9Yh4646M5XluGJsUnmwoXuiXE69KDs+fQ==} - only-allow@1.2.1: resolution: {integrity: sha512-M7CJbmv7UCopc0neRKdzfoGWaVZC+xC1925GitKH9EAqYFzX9//25Q7oX4+jw0tiCCj+t5l6VZh8UPH23NZkMA==} hasBin: true @@ -5669,9 +5605,6 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} - property-information@6.5.0: - resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} - proto3-json-serializer@2.0.1: resolution: {integrity: sha512-8awBvjO+FwkMd6gNoGFZyqkHZXCFd54CIYTb6De7dPaufGJ2XNW+QUNqbMr8MaAocMdb+KpsD4rxEOaTBDCffA==} engines: {node: '>=14.0.0'} @@ -5759,15 +5692,6 @@ packages: resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==} engines: {node: '>= 14.16.0'} - regex-recursion@4.2.1: - resolution: {integrity: sha512-QHNZyZAeKdndD1G3bKAbBEKOSSK4KOHQrAJ01N1LJeb0SoH4DJIeFhp0uUpETgONifS4+P3sOgoA1dhzgrQvhA==} - - regex-utilities@2.3.0: - resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} - - regex@5.0.2: - resolution: {integrity: sha512-/pczGbKIQgfTMRV0XjABvc5RzLqQmwqxLHdQao2RTXPk+pmTXB2P0IaUHYdYyk412YLwUIkaeMd5T+RzVgTqnQ==} - regexp.prototype.flags@1.5.2: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} @@ -5890,9 +5814,6 @@ packages: shell-quote@1.8.1: resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - shiki@1.23.1: - resolution: {integrity: sha512-8kxV9TH4pXgdKGxNOkrSMydn1Xf6It8lsle0fiqxf7a1149K1WGtdOu3Zb91T5r1JpvRPxqxU3C2XdZZXQnrig==} - shimmer@1.2.1: resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} @@ -5941,9 +5862,6 @@ packages: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} - space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -6006,9 +5924,6 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -6125,9 +6040,6 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - triple-beam@1.4.1: resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} engines: {node: '>= 14.0.0'} @@ -6226,29 +6138,29 @@ packages: resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} engines: {node: '>= 0.4'} - typedoc-material-theme@1.1.0: - resolution: {integrity: sha512-LLWGVb8w+i+QGnsu/a0JKjcuzndFQt/UeGVOQz0HFFGGocROEHv5QYudIACrj+phL2LDwH05tJx0Ob3pYYH2UA==} - engines: {node: '>=18.0.0', npm: '>=8.6.0'} + typedoc-github-theme@0.2.0: + resolution: {integrity: sha512-ycTjNm7PZharoYg67oAFDC4JUvIqvFCLp4AiCKQBhlCsbiWiMYGEAPnuoRmV6+/CQsmH2l3dfOUiwM11ZtSGqA==} + engines: {node: '>=18.0.0'} peerDependencies: - typedoc: ^0.25.13 || ^0.26.3 + typedoc: ^0.27.1 - typedoc-plugin-markdown@4.2.10: - resolution: {integrity: sha512-PLX3pc1/7z13UJm4TDE9vo9jWGcClFUErXXtd5LdnoLjV6mynPpqZLU992DwMGFSRqJFZeKbVyqlNNeNHnk2tQ==} + typedoc-plugin-markdown@4.3.2: + resolution: {integrity: sha512-hCF3V0axzbzGDYFW21XigWIJQBOJ2ZRVWWs7X+e62ew/pXnvz7iKF/zVdkBm3w8Mk4bmXWp/FT0IF4Zn9uBRww==} engines: {node: '>= 18'} peerDependencies: - typedoc: 0.26.x + typedoc: 0.27.x - typedoc-plugin-zod@1.2.1: - resolution: {integrity: sha512-oPo0PhcryKNR9UYZ6F4LFqDpQEBtNRQe6CpRTOUGrdqZOeoIZtJBVVZnSn/pBJiBU+Y6iZ/HsPESLZn1BPeLkw==} + typedoc-plugin-zod@1.3.1: + resolution: {integrity: sha512-u4NH1Ez168gRNnhUd0x4pZhp85maJ9y050IxSok9XwdzTpUA9NN0ee3ho8ssrzmxsvO2UDbDEiks7xtI0p6UXA==} peerDependencies: - typedoc: 0.23.x || 0.24.x || 0.25.x || 0.26.x + typedoc: 0.23.x || 0.24.x || 0.25.x || 0.26.x || 0.27.x - typedoc@0.26.11: - resolution: {integrity: sha512-sFEgRRtrcDl2FxVP58Ze++ZK2UQAEvtvvH8rRlig1Ja3o7dDaMHmaBfvJmdGnNEFaLTpQsN8dpvZaTqJSu/Ugw==} + typedoc@0.27.5: + resolution: {integrity: sha512-x+fhKJtTg4ozXwKayh/ek4wxZQI/+2hmZUdO2i2NGDBRUflDble70z+ewHod3d4gRpXSO6fnlnjbDTnJk7HlkQ==} engines: {node: '>= 18'} hasBin: true peerDependencies: - typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x + typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x typescript@4.9.5: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} @@ -6292,21 +6204,6 @@ packages: undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - unist-util-is@6.0.0: - resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} - - unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-parents@6.0.1: - resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} - - unist-util-visit@5.0.0: - resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -6362,12 +6259,6 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vfile-message@4.0.2: - resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} - - vfile@6.0.3: - resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} @@ -6506,9 +6397,6 @@ packages: zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - snapshots: '@ampproject/remapping@2.3.0': @@ -6949,9 +6837,9 @@ snapshots: dependencies: tslib: 2.6.2 - '@genkit-ai/ai@1.0.0-dev.0': + '@genkit-ai/ai@1.0.0-rc.0': dependencies: - '@genkit-ai/core': 1.0.0-dev.0 + '@genkit-ai/core': 1.0.0-rc.0 '@opentelemetry/api': 1.9.0 '@types/node': 20.16.9 colorette: 2.0.20 @@ -6962,7 +6850,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@genkit-ai/core@1.0.0-dev.0': + '@genkit-ai/core@1.0.0-rc.0': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) @@ -6983,6 +6871,12 @@ snapshots: transitivePeerDependencies: - supports-color + '@gerrit0/mini-shiki@1.24.4': + dependencies: + '@shikijs/engine-oniguruma': 1.24.2 + '@shikijs/types': 1.24.2 + '@shikijs/vscode-textmate': 9.3.1 + '@google-cloud/aiplatform@3.25.0(encoding@0.1.13)': dependencies: google-gax: 4.3.7(encoding@0.1.13) @@ -7442,8 +7336,6 @@ snapshots: '@langchain/core': 0.1.61 js-tiktoken: 1.0.11 - '@material/material-color-utilities@0.2.7': {} - '@mistralai/mistralai-gcp@1.3.5(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.23.8)': dependencies: google-auth-library: 9.14.2(encoding@0.1.13) @@ -8178,32 +8070,17 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.25.0': optional: true - '@shikijs/core@1.23.1': - dependencies: - '@shikijs/engine-javascript': 1.23.1 - '@shikijs/engine-oniguruma': 1.23.1 - '@shikijs/types': 1.23.1 - '@shikijs/vscode-textmate': 9.3.0 - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.3 - - '@shikijs/engine-javascript@1.23.1': - dependencies: - '@shikijs/types': 1.23.1 - '@shikijs/vscode-textmate': 9.3.0 - oniguruma-to-es: 0.4.1 - - '@shikijs/engine-oniguruma@1.23.1': + '@shikijs/engine-oniguruma@1.24.2': dependencies: - '@shikijs/types': 1.23.1 - '@shikijs/vscode-textmate': 9.3.0 + '@shikijs/types': 1.24.2 + '@shikijs/vscode-textmate': 9.3.1 - '@shikijs/types@1.23.1': + '@shikijs/types@1.24.2': dependencies: - '@shikijs/vscode-textmate': 9.3.0 + '@shikijs/vscode-textmate': 9.3.1 '@types/hast': 3.0.4 - '@shikijs/vscode-textmate@9.3.0': {} + '@shikijs/vscode-textmate@9.3.1': {} '@sinclair/typebox@0.27.8': {} @@ -8323,10 +8200,6 @@ snapshots: '@types/long@4.0.2': {} - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - '@types/memcached@2.2.10': dependencies: '@types/node': 20.16.9 @@ -8426,8 +8299,6 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@ungap/structured-clone@1.2.0': {} - abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -8703,8 +8574,6 @@ snapshots: simple-get: 3.1.1 optional: true - ccount@2.0.1: {} - chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -8718,10 +8587,6 @@ snapshots: char-regex@1.0.2: {} - character-entities-html4@2.1.0: {} - - character-entities-legacy@3.0.0: {} - charenc@0.0.2: {} chokidar@4.0.1: @@ -8797,8 +8662,6 @@ snapshots: dependencies: delayed-stream: 1.0.0 - comma-separated-tokens@2.0.3: {} - commander@10.0.1: {} commander@4.1.1: {} @@ -8965,8 +8828,6 @@ snapshots: depd@2.0.0: {} - dequal@2.0.3: {} - destroy@1.2.0: {} detect-libc@2.0.3: @@ -8974,10 +8835,6 @@ snapshots: detect-newline@3.1.0: {} - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - diff-sequences@29.6.3: {} digest-fetch@1.3.0: @@ -9016,8 +8873,6 @@ snapshots: emittery@0.13.1: {} - emoji-regex-xs@1.0.0: {} - emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -9488,10 +9343,10 @@ snapshots: - encoding - supports-color - genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-dev.0)(@genkit-ai/core@1.0.0-dev.0): + genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-rc.0)(@genkit-ai/core@1.0.0-rc.0): dependencies: - '@genkit-ai/ai': 1.0.0-dev.0 - '@genkit-ai/core': 1.0.0-dev.0 + '@genkit-ai/ai': 1.0.0-rc.0 + '@genkit-ai/core': 1.0.0-rc.0 openai: 4.53.0(encoding@0.1.13) zod: 3.23.8 transitivePeerDependencies: @@ -9760,30 +9615,10 @@ snapshots: dependencies: function-bind: 1.1.2 - hast-util-to-html@9.0.3: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - ccount: 2.0.1 - comma-separated-tokens: 2.0.3 - hast-util-whitespace: 3.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.2.0 - property-information: 6.5.0 - space-separated-tokens: 2.0.2 - stringify-entities: 4.0.4 - zwitch: 2.0.4 - - hast-util-whitespace@3.0.0: - dependencies: - '@types/hast': 3.0.4 - hosted-git-info@2.8.9: {} html-escaper@2.0.2: {} - html-void-elements@3.0.0: {} - http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -10702,18 +10537,6 @@ snapshots: crypt: 0.0.2 is-buffer: 1.1.6 - mdast-util-to-hast@13.2.0: - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.2.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.1 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.3 - mdurl@2.0.0: {} media-typer@0.3.0: {} @@ -10726,23 +10549,6 @@ snapshots: methods@1.1.2: {} - micromark-util-character@2.1.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.1 - - micromark-util-encode@2.0.1: {} - - micromark-util-sanitize-uri@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-encode: 2.0.1 - micromark-util-symbol: 2.0.1 - - micromark-util-symbol@2.0.1: {} - - micromark-util-types@2.0.1: {} - micromatch@4.0.5: dependencies: braces: 3.0.2 @@ -10933,12 +10739,6 @@ snapshots: dependencies: mimic-fn: 2.1.0 - oniguruma-to-es@0.4.1: - dependencies: - emoji-regex-xs: 1.0.0 - regex: 5.0.2 - regex-recursion: 4.2.1 - only-allow@1.2.1: dependencies: which-pm-runs: 1.1.0 @@ -11155,8 +10955,6 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 - property-information@6.5.0: {} - proto3-json-serializer@2.0.1: dependencies: protobufjs: 7.3.2 @@ -11277,16 +11075,6 @@ snapshots: readdirp@4.0.2: {} - regex-recursion@4.2.1: - dependencies: - regex-utilities: 2.3.0 - - regex-utilities@2.3.0: {} - - regex@5.0.2: - dependencies: - regex-utilities: 2.3.0 - regexp.prototype.flags@1.5.2: dependencies: call-bind: 1.0.7 @@ -11448,15 +11236,6 @@ snapshots: shell-quote@1.8.1: {} - shiki@1.23.1: - dependencies: - '@shikijs/core': 1.23.1 - '@shikijs/engine-javascript': 1.23.1 - '@shikijs/engine-oniguruma': 1.23.1 - '@shikijs/types': 1.23.1 - '@shikijs/vscode-textmate': 9.3.0 - '@types/hast': 3.0.4 - shimmer@1.2.1: {} side-channel@1.0.6: @@ -11509,8 +11288,6 @@ snapshots: dependencies: whatwg-url: 7.1.0 - space-separated-tokens@2.0.2: {} - spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 @@ -11588,11 +11365,6 @@ snapshots: dependencies: safe-buffer: 5.2.1 - stringify-entities@4.0.4: - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -11714,8 +11486,6 @@ snapshots: tree-kill@1.2.2: {} - trim-lines@3.0.1: {} - triple-beam@1.4.1: {} ts-interface-checker@0.1.13: {} @@ -11903,25 +11673,24 @@ snapshots: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - typedoc-material-theme@1.1.0(typedoc@0.26.11(typescript@4.9.5)): + typedoc-github-theme@0.2.0(typedoc@0.27.5(typescript@4.9.5)): dependencies: - '@material/material-color-utilities': 0.2.7 - typedoc: 0.26.11(typescript@4.9.5) + typedoc: 0.27.5(typescript@4.9.5) - typedoc-plugin-markdown@4.2.10(typedoc@0.26.11(typescript@4.9.5)): + typedoc-plugin-markdown@4.3.2(typedoc@0.27.5(typescript@4.9.5)): dependencies: - typedoc: 0.26.11(typescript@4.9.5) + typedoc: 0.27.5(typescript@4.9.5) - typedoc-plugin-zod@1.2.1(typedoc@0.26.11(typescript@4.9.5)): + typedoc-plugin-zod@1.3.1(typedoc@0.27.5(typescript@4.9.5)): dependencies: - typedoc: 0.26.11(typescript@4.9.5) + typedoc: 0.27.5(typescript@4.9.5) - typedoc@0.26.11(typescript@4.9.5): + typedoc@0.27.5(typescript@4.9.5): dependencies: + '@gerrit0/mini-shiki': 1.24.4 lunr: 2.3.9 markdown-it: 14.1.0 minimatch: 9.0.5 - shiki: 1.23.1 typescript: 4.9.5 yaml: 2.6.1 @@ -11951,29 +11720,6 @@ snapshots: undici-types@6.19.8: {} - unist-util-is@6.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@6.0.1: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.0 - - unist-util-visit@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 - unpipe@1.0.0: {} update-browserslist-db@1.1.1(browserslist@4.24.0): @@ -12019,16 +11765,6 @@ snapshots: vary@1.1.2: {} - vfile-message@4.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-stringify-position: 4.0.0 - - vfile@6.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile-message: 4.0.2 - walker@1.0.8: dependencies: makeerror: 1.0.12 @@ -12169,5 +11905,3 @@ snapshots: zod@3.22.4: {} zod@3.23.8: {} - - zwitch@2.0.4: {} diff --git a/js/typedoc.json b/js/typedoc.json index 1d29b99b1..272933b3f 100644 --- a/js/typedoc.json +++ b/js/typedoc.json @@ -1,10 +1,33 @@ { - "entryPoints": ["genkit", "plugins/*"], - "name": "Genkit TypeScript reference", + "entryPoints": [ + "genkit", + "plugins/googleai", + "plugins/vertexai", + "plugins/firebase", + "plugins/google-cloud", + "plugins/checks", + "plugins/dev-local-vectorstore", + "plugins/evaluators", + "plugins/express", + "plugins/ollama", + "plugins/chroma", + "plugins/pinecone", + "plugins/mcp" + ], + "name": "Genkit JS API reference", "out": "api-refs-js", "entryPointStrategy": "packages", "includeVersion": false, "sort": ["kind", "instance-first", "alphabetical"], "sortEntryPoints": true, - "plugin": ["typedoc-plugin-zod", "typedoc-material-theme"] + "readme": "index.typedoc.md", + "plugin": ["typedoc-plugin-zod", "typedoc-github-theme"], + "navigation": { + "includeCategories": false, + "includeGroups": false, + "includeFolders": false, + "compactFolders": false, + "excludeReferences": true + }, + "categorizeByGroup": false } From b922c3c6a224b71f4d7611e5074ed2309177ecad Mon Sep 17 00:00:00 2001 From: Andrew Brook Date: Wed, 18 Dec 2024 15:20:10 -0500 Subject: [PATCH 140/562] docs: update monitoring dashboard links (#1542) * docs update * fix link * remove public preview reference * minor wording --- docs/monitoring.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/monitoring.md b/docs/monitoring.md index 877c9362c..5a920277f 100644 --- a/docs/monitoring.md +++ b/docs/monitoring.md @@ -5,7 +5,7 @@ Firebase Genkit is fully instrumented with ## Telemetry Configuration -Genkit automatically manages tracing and metrics without requiring explicit configuration. You can enable telemetry exports for Firebase or Google Cloud using their respective plugins and helper functions. Using either plugin poweres the [Firebase AI Monitoring dashboard (private preview)](https://forms.gle/Lp5S1NxbZUXsWc457) that has an AI-idiomatic view of telemetry data. +Genkit automatically manages tracing and metrics without requiring explicit configuration. You can enable telemetry exports for Firebase or Google Cloud using their respective plugins and helper functions. Using either plugin powers the [Genkit Monitoring dashboard](https://console.firebase.google.com/project/_/genai_monitoring) that has an AI-centric view of telemetry data. ### For Firebase: From 97d8d151ea381e59f77d9fd90634491f933b5557 Mon Sep 17 00:00:00 2001 From: thedmail Date: Wed, 18 Dec 2024 13:16:53 -0800 Subject: [PATCH 141/562] I1521 (#1540) * docs: Deletes the Genkit sample apps, as they migrate from here to the Firebase quickstart-nodejs repo. A companion PR, in branch i1521-2 of that repo, places te files there. * docs: Deletes the Genkit sample apps, as they migrate from here to the Firebase quickstart-nodejs repo. A companion PR, in branch i1521-2 of that repo, places te files there. * docs: Deletes the Genkit sample apps, as they migrate from here to the Firebase quickstart-nodejs repo. A companion PR, in branch i1521-2 of that repo, places te files there. --- samples/README.md | 10 +- samples/chatbot/.gitignore | 1 - samples/chatbot/README.md | 35 ---- samples/chatbot/eval.json | 10 -- samples/chatbot/genkit-app/.editorconfig | 16 -- samples/chatbot/genkit-app/.gitignore | 42 ----- samples/chatbot/genkit-app/README.md | 27 --- samples/chatbot/genkit-app/angular.json | 105 ------------ samples/chatbot/genkit-app/package.json | 44 ----- samples/chatbot/genkit-app/public/favicon.ico | Bin 15086 -> 0 bytes .../genkit-app/src/app/app.component.html | 36 ---- .../genkit-app/src/app/app.component.scss | 67 -------- .../genkit-app/src/app/app.component.spec.ts | 47 ------ .../genkit-app/src/app/app.component.ts | 45 ----- .../chatbot/genkit-app/src/app/app.config.ts | 31 ---- .../chatbot/genkit-app/src/app/app.routes.ts | 26 --- .../samples/chatbot/chatbot.component.html | 58 ------- .../samples/chatbot/chatbot.component.scss | 75 --------- .../samples/chatbot/chatbot.component.spec.ts | 38 ----- .../app/samples/chatbot/chatbot.component.ts | 157 ----------------- samples/chatbot/genkit-app/src/index.html | 35 ---- samples/chatbot/genkit-app/src/main.ts | 23 --- samples/chatbot/genkit-app/src/styles.scss | 55 ------ samples/chatbot/genkit-app/tsconfig.app.json | 10 -- samples/chatbot/genkit-app/tsconfig.json | 29 ---- samples/chatbot/genkit-app/tsconfig.spec.json | 9 - samples/chatbot/package.json | 17 -- samples/chatbot/server/package.json | 29 ---- samples/chatbot/server/src/index.ts | 100 ----------- samples/chatbot/server/src/memory.ts | 35 ---- samples/chatbot/server/tsconfig.json | 14 -- samples/js-angular/.gitignore | 1 - samples/js-angular/README.md | 24 --- samples/js-angular/genkit-app/.editorconfig | 16 -- samples/js-angular/genkit-app/.gitignore | 42 ----- samples/js-angular/genkit-app/README.md | 27 --- samples/js-angular/genkit-app/angular.json | 99 ----------- samples/js-angular/genkit-app/package.json | 41 ----- .../js-angular/genkit-app/public/favicon.ico | Bin 15086 -> 0 bytes .../genkit-app/src/app/app.component.html | 36 ---- .../genkit-app/src/app/app.component.scss | 67 -------- .../genkit-app/src/app/app.component.spec.ts | 47 ------ .../genkit-app/src/app/app.component.ts | 45 ----- .../genkit-app/src/app/app.config.ts | 29 ---- .../genkit-app/src/app/app.routes.ts | 36 ---- .../src/app/home/home.component.html | 32 ---- .../src/app/home/home.component.scss | 19 --- .../src/app/home/home.component.spec.ts | 38 ----- .../genkit-app/src/app/home/home.component.ts | 27 --- .../samples/chatbot/chatbot.component.html | 77 --------- .../samples/chatbot/chatbot.component.scss | 55 ------ .../samples/chatbot/chatbot.component.spec.ts | 38 ----- .../app/samples/chatbot/chatbot.component.ts | 158 ------------------ .../streaming-json.component.html | 36 ---- .../streaming-json.component.scss | 23 --- .../streaming-json.component.spec.ts | 38 ----- .../streaming-json.component.ts | 61 ------- samples/js-angular/genkit-app/src/index.html | 35 ---- samples/js-angular/genkit-app/src/main.ts | 23 --- samples/js-angular/genkit-app/src/styles.scss | 55 ------ .../js-angular/genkit-app/tsconfig.app.json | 10 -- samples/js-angular/genkit-app/tsconfig.json | 29 ---- .../js-angular/genkit-app/tsconfig.spec.json | 9 - samples/js-angular/package.json | 17 -- samples/js-angular/server/package.json | 27 --- samples/js-angular/server/src/agent.ts | 111 ------------ samples/js-angular/server/src/chatbot.ts | 75 --------- samples/js-angular/server/src/genkit.ts | 22 --- samples/js-angular/server/src/index.ts | 23 --- .../js-angular/server/src/jsonStreaming.ts | 80 --------- samples/js-angular/server/tsconfig.json | 14 -- samples/js-coffee-shop/README.md | 6 - samples/js-coffee-shop/package.json | 35 ---- samples/js-coffee-shop/src/index.ts | 129 -------------- samples/js-coffee-shop/src/input.json | 7 - samples/js-coffee-shop/tsconfig.json | 15 -- samples/js-menu/README.md | 27 --- samples/js-menu/data/menu.jpeg | Bin 493635 -> 0 bytes samples/js-menu/data/menu.json | 97 ----------- samples/js-menu/package.json | 30 ---- samples/js-menu/src/01/example.json | 5 - samples/js-menu/src/01/prompts.ts | 87 ---------- samples/js-menu/src/02/example.json | 3 - samples/js-menu/src/02/flows.ts | 33 ---- samples/js-menu/src/02/prompts.ts | 45 ----- samples/js-menu/src/02/tools.ts | 35 ---- samples/js-menu/src/03/chats.ts | 58 ------- samples/js-menu/src/03/example.json | 4 - samples/js-menu/src/03/flows.ts | 96 ----------- samples/js-menu/src/03/prompts.ts | 47 ------ .../src/04/example.indexMenuItems.json | 57 ------- .../js-menu/src/04/example.menuQuestion.json | 3 - samples/js-menu/src/04/flows.ts | 84 ---------- samples/js-menu/src/04/prompts.ts | 44 ----- .../src/05/example.visualMenuQuestion.json | 3 - samples/js-menu/src/05/flows.ts | 96 ----------- samples/js-menu/src/05/prompts.ts | 61 ------- samples/js-menu/src/genkit.ts | 34 ---- samples/js-menu/src/index.ts | 36 ---- samples/js-menu/src/types.ts | 63 ------- samples/js-menu/tsconfig.json | 15 -- samples/js-schoolAgent/README.md | 107 ------------ samples/js-schoolAgent/package.json | 33 ---- .../js-schoolAgent/prompts/myPrompt.prompt | 1 - samples/js-schoolAgent/src/attendanceAgent.ts | 50 ------ samples/js-schoolAgent/src/data.ts | 96 ----------- samples/js-schoolAgent/src/genkit.ts | 38 ----- samples/js-schoolAgent/src/gradesAgent.ts | 53 ------ samples/js-schoolAgent/src/routingAgent.ts | 50 ------ samples/js-schoolAgent/src/terminal.ts | 123 -------------- samples/js-schoolAgent/src/tools.ts | 147 ---------------- samples/js-schoolAgent/src/types.ts | 26 --- samples/js-schoolAgent/src/util.ts | 20 --- samples/js-schoolAgent/tsconfig.json | 16 -- samples/prompts/README.md | 9 - samples/prompts/package.json | 26 --- samples/prompts/src/index.ts | 89 ---------- samples/prompts/tsconfig.json | 14 -- 118 files changed, 2 insertions(+), 5019 deletions(-) delete mode 100644 samples/chatbot/.gitignore delete mode 100644 samples/chatbot/README.md delete mode 100644 samples/chatbot/eval.json delete mode 100644 samples/chatbot/genkit-app/.editorconfig delete mode 100644 samples/chatbot/genkit-app/.gitignore delete mode 100644 samples/chatbot/genkit-app/README.md delete mode 100644 samples/chatbot/genkit-app/angular.json delete mode 100644 samples/chatbot/genkit-app/package.json delete mode 100644 samples/chatbot/genkit-app/public/favicon.ico delete mode 100644 samples/chatbot/genkit-app/src/app/app.component.html delete mode 100644 samples/chatbot/genkit-app/src/app/app.component.scss delete mode 100644 samples/chatbot/genkit-app/src/app/app.component.spec.ts delete mode 100644 samples/chatbot/genkit-app/src/app/app.component.ts delete mode 100644 samples/chatbot/genkit-app/src/app/app.config.ts delete mode 100644 samples/chatbot/genkit-app/src/app/app.routes.ts delete mode 100644 samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.html delete mode 100644 samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.scss delete mode 100644 samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts delete mode 100644 samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts delete mode 100644 samples/chatbot/genkit-app/src/index.html delete mode 100644 samples/chatbot/genkit-app/src/main.ts delete mode 100644 samples/chatbot/genkit-app/src/styles.scss delete mode 100644 samples/chatbot/genkit-app/tsconfig.app.json delete mode 100644 samples/chatbot/genkit-app/tsconfig.json delete mode 100644 samples/chatbot/genkit-app/tsconfig.spec.json delete mode 100644 samples/chatbot/package.json delete mode 100644 samples/chatbot/server/package.json delete mode 100644 samples/chatbot/server/src/index.ts delete mode 100644 samples/chatbot/server/src/memory.ts delete mode 100644 samples/chatbot/server/tsconfig.json delete mode 100644 samples/js-angular/.gitignore delete mode 100644 samples/js-angular/README.md delete mode 100644 samples/js-angular/genkit-app/.editorconfig delete mode 100644 samples/js-angular/genkit-app/.gitignore delete mode 100644 samples/js-angular/genkit-app/README.md delete mode 100644 samples/js-angular/genkit-app/angular.json delete mode 100644 samples/js-angular/genkit-app/package.json delete mode 100644 samples/js-angular/genkit-app/public/favicon.ico delete mode 100644 samples/js-angular/genkit-app/src/app/app.component.html delete mode 100644 samples/js-angular/genkit-app/src/app/app.component.scss delete mode 100644 samples/js-angular/genkit-app/src/app/app.component.spec.ts delete mode 100644 samples/js-angular/genkit-app/src/app/app.component.ts delete mode 100644 samples/js-angular/genkit-app/src/app/app.config.ts delete mode 100644 samples/js-angular/genkit-app/src/app/app.routes.ts delete mode 100644 samples/js-angular/genkit-app/src/app/home/home.component.html delete mode 100644 samples/js-angular/genkit-app/src/app/home/home.component.scss delete mode 100644 samples/js-angular/genkit-app/src/app/home/home.component.spec.ts delete mode 100644 samples/js-angular/genkit-app/src/app/home/home.component.ts delete mode 100644 samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.html delete mode 100644 samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.scss delete mode 100644 samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts delete mode 100644 samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts delete mode 100644 samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.html delete mode 100644 samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.scss delete mode 100644 samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.spec.ts delete mode 100644 samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts delete mode 100644 samples/js-angular/genkit-app/src/index.html delete mode 100644 samples/js-angular/genkit-app/src/main.ts delete mode 100644 samples/js-angular/genkit-app/src/styles.scss delete mode 100644 samples/js-angular/genkit-app/tsconfig.app.json delete mode 100644 samples/js-angular/genkit-app/tsconfig.json delete mode 100644 samples/js-angular/genkit-app/tsconfig.spec.json delete mode 100644 samples/js-angular/package.json delete mode 100644 samples/js-angular/server/package.json delete mode 100644 samples/js-angular/server/src/agent.ts delete mode 100644 samples/js-angular/server/src/chatbot.ts delete mode 100644 samples/js-angular/server/src/genkit.ts delete mode 100644 samples/js-angular/server/src/index.ts delete mode 100644 samples/js-angular/server/src/jsonStreaming.ts delete mode 100644 samples/js-angular/server/tsconfig.json delete mode 100644 samples/js-coffee-shop/README.md delete mode 100644 samples/js-coffee-shop/package.json delete mode 100644 samples/js-coffee-shop/src/index.ts delete mode 100644 samples/js-coffee-shop/src/input.json delete mode 100644 samples/js-coffee-shop/tsconfig.json delete mode 100644 samples/js-menu/README.md delete mode 100644 samples/js-menu/data/menu.jpeg delete mode 100644 samples/js-menu/data/menu.json delete mode 100644 samples/js-menu/package.json delete mode 100644 samples/js-menu/src/01/example.json delete mode 100644 samples/js-menu/src/01/prompts.ts delete mode 100644 samples/js-menu/src/02/example.json delete mode 100644 samples/js-menu/src/02/flows.ts delete mode 100644 samples/js-menu/src/02/prompts.ts delete mode 100644 samples/js-menu/src/02/tools.ts delete mode 100644 samples/js-menu/src/03/chats.ts delete mode 100644 samples/js-menu/src/03/example.json delete mode 100644 samples/js-menu/src/03/flows.ts delete mode 100644 samples/js-menu/src/03/prompts.ts delete mode 100644 samples/js-menu/src/04/example.indexMenuItems.json delete mode 100644 samples/js-menu/src/04/example.menuQuestion.json delete mode 100644 samples/js-menu/src/04/flows.ts delete mode 100644 samples/js-menu/src/04/prompts.ts delete mode 100644 samples/js-menu/src/05/example.visualMenuQuestion.json delete mode 100644 samples/js-menu/src/05/flows.ts delete mode 100644 samples/js-menu/src/05/prompts.ts delete mode 100644 samples/js-menu/src/genkit.ts delete mode 100644 samples/js-menu/src/index.ts delete mode 100644 samples/js-menu/src/types.ts delete mode 100644 samples/js-menu/tsconfig.json delete mode 100644 samples/js-schoolAgent/README.md delete mode 100644 samples/js-schoolAgent/package.json delete mode 100644 samples/js-schoolAgent/prompts/myPrompt.prompt delete mode 100644 samples/js-schoolAgent/src/attendanceAgent.ts delete mode 100644 samples/js-schoolAgent/src/data.ts delete mode 100644 samples/js-schoolAgent/src/genkit.ts delete mode 100644 samples/js-schoolAgent/src/gradesAgent.ts delete mode 100644 samples/js-schoolAgent/src/routingAgent.ts delete mode 100644 samples/js-schoolAgent/src/terminal.ts delete mode 100644 samples/js-schoolAgent/src/tools.ts delete mode 100644 samples/js-schoolAgent/src/types.ts delete mode 100644 samples/js-schoolAgent/src/util.ts delete mode 100644 samples/js-schoolAgent/tsconfig.json delete mode 100644 samples/prompts/README.md delete mode 100644 samples/prompts/package.json delete mode 100644 samples/prompts/src/index.ts delete mode 100644 samples/prompts/tsconfig.json diff --git a/samples/README.md b/samples/README.md index f02ab4dd1..333412ade 100644 --- a/samples/README.md +++ b/samples/README.md @@ -1,10 +1,4 @@ # Genkit samples -Take a look at some samples of Genkit in use: - -- [js-coffee-shop](samples/js-coffee-shop/): "AI barista", demonstrating simple - LLM usage -- [js-menu](samples/js-menu/): Progressively more sophisticated versions of a - menu understanding app -- [chatbot](samples/chatbot/): A simple chatbot with a JavaScript frontend -- [js-angular](samples/js-angular/): Demo of streaming to an Angular frontend +These samples have migrated to the Firebase +[`quickstart-nodejs` repo](https://github.com/firebase/quickstart-nodejs). diff --git a/samples/chatbot/.gitignore b/samples/chatbot/.gitignore deleted file mode 100644 index 7951405f8..000000000 --- a/samples/chatbot/.gitignore +++ /dev/null @@ -1 +0,0 @@ -lib \ No newline at end of file diff --git a/samples/chatbot/README.md b/samples/chatbot/README.md deleted file mode 100644 index a2f1728bf..000000000 --- a/samples/chatbot/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Chatbot - -This is a simple chatbot. You can pick which model to use. - -Prerequisite - -- install Genkit (`npm i -g genkit`) -- Google Cloud project with Vertex AI API enabled (https://pantheon.corp.google.com/apis/library/aiplatform.googleapis.com) -- gcloud CLI installed (https://cloud.google.com/sdk/docs/install-sdk) -- to use Llama 3.1 405b enable it in the Vertex AI [Model Garden](https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama3-405b-instruct-maas) - -The sample is using Vertex AI, so you'll need to auth: - -```bash -gcloud auth login -gcloud auth application-default login --project YOUR_PROJECT -``` - -Clone this code - -``` -git clone https://github.com/firebase/genkit -cd genkit/samples/chatbot -``` - -Install deps and run the chatbot app - -```bash -npm run setup -npm start -``` - -Point your browser to http://localhost:4200/ - -Inspect runs in http://localhost:4000/ diff --git a/samples/chatbot/eval.json b/samples/chatbot/eval.json deleted file mode 100644 index ca1c84cba..000000000 --- a/samples/chatbot/eval.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "conversationId": "1234", - "prompt": "tell me a joke" - }, - { - "conversationId": "2345", - "prompt": "wtite a python program that prints out weather for the current location" - } -] diff --git a/samples/chatbot/genkit-app/.editorconfig b/samples/chatbot/genkit-app/.editorconfig deleted file mode 100644 index 59d9a3a3e..000000000 --- a/samples/chatbot/genkit-app/.editorconfig +++ /dev/null @@ -1,16 +0,0 @@ -# Editor configuration, see https://editorconfig.org -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 2 -insert_final_newline = true -trim_trailing_whitespace = true - -[*.ts] -quote_type = single - -[*.md] -max_line_length = off -trim_trailing_whitespace = false diff --git a/samples/chatbot/genkit-app/.gitignore b/samples/chatbot/genkit-app/.gitignore deleted file mode 100644 index cc7b14135..000000000 --- a/samples/chatbot/genkit-app/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. - -# Compiled output -/dist -/tmp -/out-tsc -/bazel-out - -# Node -/node_modules -npm-debug.log -yarn-error.log - -# IDEs and editors -.idea/ -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# Visual Studio Code -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.history/* - -# Miscellaneous -/.angular/cache -.sass-cache/ -/connect.lock -/coverage -/libpeerconnection.log -testem.log -/typings - -# System files -.DS_Store -Thumbs.db diff --git a/samples/chatbot/genkit-app/README.md b/samples/chatbot/genkit-app/README.md deleted file mode 100644 index 0aeb2095b..000000000 --- a/samples/chatbot/genkit-app/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# GenkitApp - -This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 18.0.2. - -## Development server - -Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. - -## Code scaffolding - -Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. - -## Build - -Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. - -## Running unit tests - -Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). - -## Running end-to-end tests - -Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. - -## Further help - -To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/samples/chatbot/genkit-app/angular.json b/samples/chatbot/genkit-app/angular.json deleted file mode 100644 index 07bb6ad5f..000000000 --- a/samples/chatbot/genkit-app/angular.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "newProjectRoot": "projects", - "projects": { - "genkit-app": { - "projectType": "application", - "schematics": { - "@schematics/angular:component": { - "style": "scss" - } - }, - "root": "", - "sourceRoot": "src", - "prefix": "app", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:application", - "options": { - "outputPath": "dist/genkit-app", - "index": "src/index.html", - "browser": "src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - { - "glob": "**/*", - "input": "public" - } - ], - "styles": [ - "@angular/material/prebuilt-themes/azure-blue.css", - "src/styles.scss", - "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.css" - ], - "scripts": [ - "node_modules/prismjs/prism.js", - "node_modules/prismjs/components/prism-csharp.min.js", - "node_modules/prismjs/components/prism-css.min.js", - "node_modules/prismjs/plugins/line-highlight/prism-line-highlight.js" - ] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kB", - "maximumError": "1MB" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kB", - "maximumError": "4kB" - } - ], - "outputHashing": "all" - }, - "development": { - "optimization": false, - "extractLicenses": false, - "sourceMap": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "builder": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "genkit-app:build:production" - }, - "development": { - "buildTarget": "genkit-app:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n" - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "polyfills": ["zone.js", "zone.js/testing"], - "tsConfig": "tsconfig.spec.json", - "inlineStyleLanguage": "scss", - "assets": [ - { - "glob": "**/*", - "input": "public" - } - ], - "styles": [ - "@angular/material/prebuilt-themes/azure-blue.css", - "src/styles.scss" - ], - "scripts": [] - } - } - } - } - } -} diff --git a/samples/chatbot/genkit-app/package.json b/samples/chatbot/genkit-app/package.json deleted file mode 100644 index 465379b0b..000000000 --- a/samples/chatbot/genkit-app/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "genkit-app", - "version": "0.0.0", - "scripts": { - "ng": "ng", - "start": "ng serve", - "build": "ng build", - "watch": "ng build --watch --configuration development", - "test": "ng test" - }, - "private": true, - "dependencies": { - "@angular/animations": "^18.0.0", - "@angular/cdk": "^18.0.1", - "@angular/common": "^18.0.0", - "@angular/compiler": "^18.0.0", - "@angular/core": "^18.0.0", - "@angular/forms": "^18.0.0", - "@angular/material": "^18.0.1", - "@angular/platform-browser": "^18.0.0", - "@angular/platform-browser-dynamic": "^18.0.0", - "@angular/router": "^18.0.0", - "marked": "^12.0.2", - "ngx-markdown": "^18.0.0", - "prismjs": "^1.29.0", - "rxjs": "~7.8.0", - "tslib": "^2.3.0", - "zone.js": "~0.14.3", - "genkit": "^0.9.0-rc || ^0.9" - }, - "devDependencies": { - "@angular-devkit/build-angular": "^18.0.2", - "@angular/cli": "^18.0.2", - "@angular/compiler-cli": "^18.0.0", - "@types/jasmine": "~5.1.0", - "jasmine-core": "~5.1.0", - "karma": "~6.4.0", - "karma-chrome-launcher": "~3.2.0", - "karma-coverage": "~2.2.0", - "karma-jasmine": "~5.1.0", - "karma-jasmine-html-reporter": "~2.1.0", - "typescript": "~5.4.2" - } -} diff --git a/samples/chatbot/genkit-app/public/favicon.ico b/samples/chatbot/genkit-app/public/favicon.ico deleted file mode 100644 index 57614f9c967596fad0a3989bec2b1deff33034f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 zcmd^G33O9Omi+`8$@{|M-I6TH3wzF-p5CV8o}7f~KxR60LK+ApEFB<$bcciv%@SmA zV{n>g85YMFFeU*Uvl=i4v)C*qgnb;$GQ=3XTe9{Y%c`mO%su)noNCCQ*@t1WXn|B(hQ7i~ zrUK8|pUkD6#lNo!bt$6)jR!&C?`P5G(`e((P($RaLeq+o0Vd~f11;qB05kdbAOm?r zXv~GYr_sibQO9NGTCdT;+G(!{4Xs@4fPak8#L8PjgJwcs-Mm#nR_Z0s&u?nDX5^~@ z+A6?}g0|=4e_LoE69pPFO`yCD@BCjgKpzMH0O4Xs{Ahc?K3HC5;l=f zg>}alhBXX&);z$E-wai+9TTRtBX-bWYY@cl$@YN#gMd~tM_5lj6W%8ah4;uZ;jP@Q zVbuel1rPA?2@x9Y+u?e`l{Z4ngfG5q5BLH5QsEu4GVpt{KIp1?U)=3+KQ;%7ec8l* zdV=zZgN5>O3G(3L2fqj3;oBbZZw$Ij@`Juz@?+yy#OPw)>#wsTewVgTK9BGt5AbZ&?K&B3GVF&yu?@(Xj3fR3n+ZP0%+wo)D9_xp>Z$`A4 zfV>}NWjO#3lqumR0`gvnffd9Ka}JJMuHS&|55-*mCD#8e^anA<+sFZVaJe7{=p*oX zE_Uv?1>e~ga=seYzh{9P+n5<+7&9}&(kwqSaz;1aD|YM3HBiy<))4~QJSIryyqp| z8nGc(8>3(_nEI4n)n7j(&d4idW1tVLjZ7QbNLXg;LB ziHsS5pXHEjGJZb59KcvS~wv;uZR-+4qEqow`;JCfB*+b^UL^3!?;-^F%yt=VjU|v z39SSqKcRu_NVvz!zJzL0CceJaS6%!(eMshPv_0U5G`~!a#I$qI5Ic(>IONej@aH=f z)($TAT#1I{iCS4f{D2+ApS=$3E7}5=+y(rA9mM#;Cky%b*Gi0KfFA`ofKTzu`AV-9 znW|y@19rrZ*!N2AvDi<_ZeR3O2R{#dh1#3-d%$k${Rx42h+i&GZo5!C^dSL34*AKp z27mTd>k>?V&X;Nl%GZ(>0s`1UN~Hfyj>KPjtnc|)xM@{H_B9rNr~LuH`Gr5_am&Ep zTjZA8hljNj5H1Ipm-uD9rC}U{-vR!eay5&6x6FkfupdpT*84MVwGpdd(}ib)zZ3Ky z7C$pnjc82(W_y_F{PhYj?o!@3__UUvpX)v69aBSzYj3 zdi}YQkKs^SyXyFG2LTRz9{(w}y~!`{EuAaUr6G1M{*%c+kP1olW9z23dSH!G4_HSK zzae-DF$OGR{ofP*!$a(r^5Go>I3SObVI6FLY)N@o<*gl0&kLo-OT{Tl*7nCz>Iq=? zcigIDHtj|H;6sR?or8Wd_a4996GI*CXGU}o;D9`^FM!AT1pBY~?|4h^61BY#_yIfO zKO?E0 zJ{Pc`9rVEI&$xxXu`<5E)&+m(7zX^v0rqofLs&bnQT(1baQkAr^kEsk)15vlzAZ-l z@OO9RF<+IiJ*O@HE256gCt!bF=NM*vh|WVWmjVawcNoksRTMvR03H{p@cjwKh(CL4 z7_PB(dM=kO)!s4fW!1p0f93YN@?ZSG` z$B!JaAJCtW$B97}HNO9(x-t30&E}Mo1UPi@Av%uHj~?T|!4JLwV;KCx8xO#b9IlUW zI6+{a@Wj|<2Y=U;a@vXbxqZNngH8^}LleE_4*0&O7#3iGxfJ%Id>+sb;7{L=aIic8 z|EW|{{S)J-wr@;3PmlxRXU8!e2gm_%s|ReH!reFcY8%$Hl4M5>;6^UDUUae?kOy#h zk~6Ee_@ZAn48Bab__^bNmQ~+k=02jz)e0d9Z3>G?RGG!65?d1>9}7iG17?P*=GUV-#SbLRw)Hu{zx*azHxWkGNTWl@HeWjA?39Ia|sCi{e;!^`1Oec zb>Z|b65OM*;eC=ZLSy?_fg$&^2xI>qSLA2G*$nA3GEnp3$N-)46`|36m*sc#4%C|h zBN<2U;7k>&G_wL4=Ve5z`ubVD&*Hxi)r@{4RCDw7U_D`lbC(9&pG5C*z#W>8>HU)h z!h3g?2UL&sS!oY5$3?VlA0Me9W5e~V;2jds*fz^updz#AJ%G8w2V}AEE?E^=MK%Xt z__Bx1cr7+DQmuHmzn*|hh%~eEc9@m05@clWfpEFcr+06%0&dZJH&@8^&@*$qR@}o3 z@Tuuh2FsLz^zH+dN&T&?0G3I?MpmYJ;GP$J!EzjeM#YLJ!W$}MVNb0^HfOA>5Fe~UNn%Zk(PT@~9}1dt)1UQ zU*B5K?Dl#G74qmg|2>^>0WtLX#Jz{lO4NT`NYB*(L#D|5IpXr9v&7a@YsGp3vLR7L zHYGHZg7{ie6n~2p$6Yz>=^cEg7tEgk-1YRl%-s7^cbqFb(U7&Dp78+&ut5!Tn(hER z|Gp4Ed@CnOPeAe|N>U(dB;SZ?NU^AzoD^UAH_vamp6Ws}{|mSq`^+VP1g~2B{%N-!mWz<`)G)>V-<`9`L4?3dM%Qh6<@kba+m`JS{Ya@9Fq*m6$$ zA1%Ogc~VRH33|S9l%CNb4zM%k^EIpqY}@h{w(aBcJ9c05oiZx#SK9t->5lSI`=&l~ z+-Ic)a{FbBhXV$Xt!WRd`R#Jk-$+_Z52rS>?Vpt2IK<84|E-SBEoIw>cs=a{BlQ7O z-?{Fy_M&84&9|KM5wt~)*!~i~E=(6m8(uCO)I=)M?)&sRbzH$9Rovzd?ZEY}GqX+~ zFbEbLz`BZ49=2Yh-|<`waK-_4!7`ro@zlC|r&I4fc4oyb+m=|c8)8%tZ-z5FwhzDt zL5kB@u53`d@%nHl0Sp)Dw`(QU&>vujEn?GPEXUW!Wi<+4e%BORl&BIH+SwRcbS}X@ z01Pk|vA%OdJKAs17zSXtO55k!;%m9>1eW9LnyAX4uj7@${O6cfii`49qTNItzny5J zH&Gj`e}o}?xjQ}r?LrI%FjUd@xflT3|7LA|ka%Q3i}a8gVm<`HIWoJGH=$EGClX^C0lysQJ>UO(q&;`T#8txuoQ_{l^kEV9CAdXuU1Ghg8 zN_6hHFuy&1x24q5-(Z7;!poYdt*`UTdrQOIQ!2O7_+AHV2hgXaEz7)>$LEdG z<8vE^Tw$|YwZHZDPM!SNOAWG$?J)MdmEk{U!!$M#fp7*Wo}jJ$Q(=8>R`Ats?e|VU?Zt7Cdh%AdnfyN3MBWw{ z$OnREvPf7%z6`#2##_7id|H%Y{vV^vWXb?5d5?a_y&t3@p9t$ncHj-NBdo&X{wrfJ zamN)VMYROYh_SvjJ=Xd!Ga?PY_$;*L=SxFte!4O6%0HEh%iZ4=gvns7IWIyJHa|hT z2;1+e)`TvbNb3-0z&DD_)Jomsg-7p_Uh`wjGnU1urmv1_oVqRg#=C?e?!7DgtqojU zWoAB($&53;TsXu^@2;8M`#z{=rPy?JqgYM0CDf4v@z=ZD|ItJ&8%_7A#K?S{wjxgd z?xA6JdJojrWpB7fr2p_MSsU4(R7=XGS0+Eg#xR=j>`H@R9{XjwBmqAiOxOL` zt?XK-iTEOWV}f>Pz3H-s*>W z4~8C&Xq25UQ^xH6H9kY_RM1$ch+%YLF72AA7^b{~VNTG}Tj#qZltz5Q=qxR`&oIlW Nr__JTFzvMr^FKp4S3v*( diff --git a/samples/chatbot/genkit-app/src/app/app.component.html b/samples/chatbot/genkit-app/src/app/app.component.html deleted file mode 100644 index 78c45a375..000000000 --- a/samples/chatbot/genkit-app/src/app/app.component.html +++ /dev/null @@ -1,36 +0,0 @@ - - -
- - Chat with an LLM - - - -
- - description - -
- -
- -
-
diff --git a/samples/chatbot/genkit-app/src/app/app.component.scss b/samples/chatbot/genkit-app/src/app/app.component.scss deleted file mode 100644 index 0e0b80557..000000000 --- a/samples/chatbot/genkit-app/src/app/app.component.scss +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright 2024 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. - */ - -app-bar { - border-bottom: 1px solid var(--divider-color); - grid-area: header; -} - -article { - grid-area: content; -} - -.wrapper { - display: grid; - grid-template: - 'header' auto - 'content' 1fr - / 1fr; - height: 100vh; -} - -.home-link { - align-items: center; - color: var(--mat-app-color); - display: flex; - gap: 8px; - - img { - height: 22px; - padding-left: 4px; - } -} - -.mat-toolbar { - background: #d7e3ff; - color: #005cbb; - gap: 4px; -} - -nav { - --mdc-secondary-navigation-tab-container-height: 64px; - --mat-tab-header-divider-height: 0; - margin-left: 32px; -} - -.preview-badge { - margin-left: 8px; - - mat-icon { - font-size: 18px; - height: 18px; - width: 18px; - } -} diff --git a/samples/chatbot/genkit-app/src/app/app.component.spec.ts b/samples/chatbot/genkit-app/src/app/app.component.spec.ts deleted file mode 100644 index 02dac7e71..000000000 --- a/samples/chatbot/genkit-app/src/app/app.component.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2024 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 { TestBed } from '@angular/core/testing'; -import { AppComponent } from './app.component'; - -describe('AppComponent', () => { - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [AppComponent], - }).compileComponents(); - }); - - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); - - it(`should have the 'genkit-app' title`, () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app.title).toEqual('genkit-app'); - }); - - it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain( - 'Hello, genkit-app' - ); - }); -}); diff --git a/samples/chatbot/genkit-app/src/app/app.component.ts b/samples/chatbot/genkit-app/src/app/app.component.ts deleted file mode 100644 index 39d4f4ea0..000000000 --- a/samples/chatbot/genkit-app/src/app/app.component.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2024 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 { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; -import { MatIconModule } from '@angular/material/icon'; -import { MatTabNavPanel, MatTabsModule } from '@angular/material/tabs'; -import { MatToolbarModule } from '@angular/material/toolbar'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; - -@Component({ - selector: 'app-root', - standalone: true, - imports: [ - CommonModule, - MatToolbarModule, - RouterOutlet, - MatIconModule, - MatTabNavPanel, - MatButtonModule, - MatTabsModule, - MatToolbarModule, - MatTooltipModule, - RouterLink, - RouterLinkActive, - ], - templateUrl: './app.component.html', - styleUrl: './app.component.scss', -}) -export class AppComponent {} diff --git a/samples/chatbot/genkit-app/src/app/app.config.ts b/samples/chatbot/genkit-app/src/app/app.config.ts deleted file mode 100644 index 7c47634a3..000000000 --- a/samples/chatbot/genkit-app/src/app/app.config.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 2024 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 { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; -import { provideRouter } from '@angular/router'; - -import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; -import { provideMarkdown } from 'ngx-markdown'; -import { routes } from './app.routes'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideZoneChangeDetection({ eventCoalescing: true }), - provideRouter(routes), - provideAnimationsAsync(), - provideMarkdown(), - ], -}; diff --git a/samples/chatbot/genkit-app/src/app/app.routes.ts b/samples/chatbot/genkit-app/src/app/app.routes.ts deleted file mode 100644 index d59a096c2..000000000 --- a/samples/chatbot/genkit-app/src/app/app.routes.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright 2024 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 { Routes } from '@angular/router'; -import { ChatbotComponent } from './samples/chatbot/chatbot.component'; - -export const routes: Routes = [ - { - path: 'home', - component: ChatbotComponent, - }, - { path: '**', redirectTo: '/home' }, -]; diff --git a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.html b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.html deleted file mode 100644 index 782825689..000000000 --- a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.html +++ /dev/null @@ -1,58 +0,0 @@ - - -
- @if (llmIndex === undefined) { -

Choose an LLM

- @for (name of llmNames; track name; let index = $index) { - - } - } @else { -

Chat with {{ llmNames[llmIndex] }}

- -
-
- {{ entry.text }} -
-
- - - -
-
- - @if (error) { -
{{ error }}
- } - -
- - Chat input. Press Enter to submit (Shift+Enter for line - break) - - - -
- } -
diff --git a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.scss b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.scss deleted file mode 100644 index 02802405d..000000000 --- a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.scss +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright 2024 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. - */ - -.wrapper { - margin-left: auto; - margin-right: auto; - padding: 20px; - width: 800px; -} - -.user-bubble { - background-color: bisque; - border: 1px solid #ccc; - border-radius: 10px; - margin-bottom: 20px; - margin-left: auto; - margin-right: 0; - min-width: 300px; - padding: 20px; - white-space: pre-wrap; - width: 80%; -} - -.model-bubble { - border: 1px solid #ccc; - border-radius: 10px; - margin-bottom: 20px; - min-width: 300px; - padding: 20px; - width: 80%; - &.llm-0 { - background-color: rgb(218, 247, 237); - } - &.llm-1 { - background-color: rgb(244, 216, 247); - } - - .text { - white-space: pre-wrap; - } - - .model-name { - font-size: small; - font-weight: 100; - margin-bottom: 11px; - } -} - -.input-field { - min-width: 400px; - vertical-align: top; - width: 730px; -} - -.error { - background-color: pink; - border: 1px solid red; - border-radius: 15px; - margin: 20px 0; - overflow: auto; - padding: 20px; -} diff --git a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts deleted file mode 100644 index c79a6e1f7..000000000 --- a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2024 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 { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ChatbotComponent } from './chatbot.component'; - -describe('ChatbotComponent', () => { - let component: ChatbotComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ChatbotComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(ChatbotComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts deleted file mode 100644 index 293bdf106..000000000 --- a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright 2024 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 { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; -import { - FormControl, - FormsModule, - ReactiveFormsModule, - Validators, -} from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { provideNativeDateAdapter } from '@angular/material/core'; -import { MatDatepickerModule } from '@angular/material/datepicker'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatIconModule } from '@angular/material/icon'; -import { MatInputModule } from '@angular/material/input'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatRadioModule } from '@angular/material/radio'; -import { streamFlow } from 'genkit/client'; -import { MarkdownModule } from 'ngx-markdown'; - -const url = 'http://127.0.0.1:3400/chatbotFlow'; - -interface ToolResponse { - name: string; - ref: string; - output?: unknown; -} - -interface InputSchema { - role: 'user'; - text?: string; - toolResponse?: ToolResponse; -} - -interface ToolRequest { - name: string; - ref: string; - input?: unknown; -} -interface OutputSchema { - role: 'model'; - text?: string; - toolRequest?: ToolRequest; -} - -@Component({ - selector: 'app-chatbot', - standalone: true, - providers: [provideNativeDateAdapter()], - imports: [ - CommonModule, - FormsModule, - MatFormFieldModule, - MatInputModule, - ReactiveFormsModule, - MatButtonModule, - MatIconModule, - MatProgressBarModule, - MatDatepickerModule, - MarkdownModule, - MatRadioModule, - ], - templateUrl: './chatbot.component.html', - styleUrl: './chatbot.component.scss', -}) -export class ChatbotComponent { - history: (InputSchema | OutputSchema)[] = []; - error?: string; - input?: string; - loading = false; - id = Date.now() + '' + Math.floor(Math.random() * 1000000000); - llmIndex: number | undefined; - - llmNames = ['Gemini 1.5 Flash', 'Llama 3.1 405b']; - - chatFormControl = new FormControl( - 'write a function that adds two number together', - [Validators.required] - ); - - ask(input?: string) { - const text = this.chatFormControl.value!.trim(); - if (!text) return; - this.history.push({ role: 'user', text: text }); - this.chatFormControl.setValue(''); - this.chatFormControl.disable(); - this.callFlow({ role: 'user', text }); - this.loading = true; - } - - async callFlow(input: InputSchema) { - this.error = undefined; - this.loading = true; - try { - const response = await streamFlow({ - url, - input: { - prompt: input, - conversationId: this.id, - llmIndex: this.llmIndex, - }, - }); - - let textBlock: OutputSchema | undefined = undefined; - for await (const chunk of response.stream()) { - for (const content of chunk.content) { - if (content.text) { - if (!textBlock) { - textBlock = { role: 'model', text: content.text! }; - this.history.push(textBlock); - } else { - textBlock.text += content.text!; - } - } - } - } - - this.loading = false; - this.chatFormControl.enable(); - - await response.output(); - } catch (e) { - this.loading = false; - this.chatFormControl.enable(); - if ((e as any).cause) { - this.error = `${(e as any).cause}`; - } else { - this.error = `${e}`; - } - } - } - - keyPress(event: KeyboardEvent) { - if (event.key === 'Enter') { - if (event.ctrlKey || event.shiftKey) { - this.input += '\n'; - } else { - this.ask(this.input); - } - } - } -} diff --git a/samples/chatbot/genkit-app/src/index.html b/samples/chatbot/genkit-app/src/index.html deleted file mode 100644 index 822a28173..000000000 --- a/samples/chatbot/genkit-app/src/index.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - GenkitApp - - - - - - - - - - diff --git a/samples/chatbot/genkit-app/src/main.ts b/samples/chatbot/genkit-app/src/main.ts deleted file mode 100644 index b1be530a2..000000000 --- a/samples/chatbot/genkit-app/src/main.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright 2024 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 { bootstrapApplication } from '@angular/platform-browser'; -import { AppComponent } from './app/app.component'; -import { appConfig } from './app/app.config'; - -bootstrapApplication(AppComponent, appConfig).catch((err) => - console.error(err) -); diff --git a/samples/chatbot/genkit-app/src/styles.scss b/samples/chatbot/genkit-app/src/styles.scss deleted file mode 100644 index 0ff0b59e1..000000000 --- a/samples/chatbot/genkit-app/src/styles.scss +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2024 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. - */ - -/* You can add global styles to this file, and also import other style files */ - -:root { - --header-height: 65px; - --container-border-radius: 20px; - --input-border-radius: 8px; -} - -html, -body { - height: 100%; -} - -body { - background-color: var(--app-background); - color: var(--mat-app-text-color); - margin: 0; -} - -hr { - border-bottom: 1px solid var(--divider-color); - border-width: 0 0 1px; - margin: 12px 0; -} - -a { - color: var(--link-color); - text-decoration: none; -} - -pre { - margin: 0; - white-space: pre-wrap; -} - -// Helper for filling available space in flex layouts -.flex-spacer { - flex: 1; -} diff --git a/samples/chatbot/genkit-app/tsconfig.app.json b/samples/chatbot/genkit-app/tsconfig.app.json deleted file mode 100644 index 84f1f992d..000000000 --- a/samples/chatbot/genkit-app/tsconfig.app.json +++ /dev/null @@ -1,10 +0,0 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "./out-tsc/app", - "types": [] - }, - "files": ["src/main.ts"], - "include": ["src/**/*.d.ts"] -} diff --git a/samples/chatbot/genkit-app/tsconfig.json b/samples/chatbot/genkit-app/tsconfig.json deleted file mode 100644 index 437984834..000000000 --- a/samples/chatbot/genkit-app/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ -{ - "compileOnSave": false, - "compilerOptions": { - "outDir": "./dist/out-tsc", - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "skipLibCheck": true, - "esModuleInterop": true, - "sourceMap": true, - "declaration": false, - "experimentalDecorators": true, - "moduleResolution": "bundler", - "importHelpers": true, - "target": "ES2022", - "module": "ES2022", - "useDefineForClassFields": false, - "lib": ["ES2022", "dom"] - }, - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true - } -} diff --git a/samples/chatbot/genkit-app/tsconfig.spec.json b/samples/chatbot/genkit-app/tsconfig.spec.json deleted file mode 100644 index 47e3dd755..000000000 --- a/samples/chatbot/genkit-app/tsconfig.spec.json +++ /dev/null @@ -1,9 +0,0 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "./out-tsc/spec", - "types": ["jasmine"] - }, - "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] -} diff --git a/samples/chatbot/package.json b/samples/chatbot/package.json deleted file mode 100644 index 5cfb52ff9..000000000 --- a/samples/chatbot/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "scripts": { - "start": "concurrently npm:start:server npm:start:ng", - "setup": "npm i && cd server && npm i && cd ../genkit-app && npm i", - "start:server": "cd server && npm run genkit:dev", - "start:ng": "cd genkit-app && npm start" - }, - "name": "js-angular", - "version": "1.0.0", - "description": "This is a simple UI for streaming RPG character generator.", - "keywords": [], - "author": "", - "license": "ISC", - "devDependencies": { - "concurrently": "^8.2.2" - } -} diff --git a/samples/chatbot/server/package.json b/samples/chatbot/server/package.json deleted file mode 100644 index 4f13d4997..000000000 --- a/samples/chatbot/server/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "main": "lib/index.js", - "scripts": { - "start": "node lib/index.js", - "genkit:dev": "genkit start -- npm run dev", - "dev": "tsx --watch src/index.ts", - "build": "tsc", - "build:watch": "tsc --watch", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "name": "js-angular", - "version": "1.0.0", - "description": "This is a simple UI for streaming RPG character generator.", - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "genkit": "^0.9.0-rc || ^0.9", - "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9", - "express": "^4.21.0", - "partial-json": "^0.1.7", - "zod": "^3.23.8" - }, - "devDependencies": { - "genkit-cli": "^0.9.0-rc || ^0.9", - "typescript": "^5.4.5", - "tsx": "^4.19.2" - } -} diff --git a/samples/chatbot/server/src/index.ts b/samples/chatbot/server/src/index.ts deleted file mode 100644 index 7bd2144bf..000000000 --- a/samples/chatbot/server/src/index.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright 2024 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 { gemini15Flash, vertexAI } from '@genkit-ai/vertexai'; -import { - VertexAIEvaluationMetricType, - vertexAIEvaluation, -} from '@genkit-ai/vertexai/evaluation'; -import { llama31, vertexAIModelGarden } from '@genkit-ai/vertexai/modelgarden'; -import { ModelReference, PartSchema, genkit, run } from 'genkit'; -import { GenerateResponseChunkSchema } from 'genkit/model'; -import { z } from 'zod'; -import { inMemoryStore } from './memory.js'; - -export const AgentInput = z.object({ - conversationId: z.string(), - prompt: z.union([z.string(), PartSchema, z.array(PartSchema)]), - config: z.record(z.string(), z.any()).optional(), - llmIndex: z.number(), -}); - -const ai = genkit({ - plugins: [ - vertexAI({ - location: 'us-central1', - }), - vertexAIModelGarden({ - location: 'us-central1', - models: [llama31], - }), - vertexAIEvaluation({ - location: 'us-central1', - metrics: [ - VertexAIEvaluationMetricType.SAFETY, - VertexAIEvaluationMetricType.FLUENCY, - ], - }), - ], -}); - -const llms: ModelReference[] = [gemini15Flash, llama31]; - -const historyStore = inMemoryStore(); - -export const chatbotFlow = ai.defineStreamingFlow( - { - name: 'chatbotFlow', - inputSchema: AgentInput, - outputSchema: z.string(), - streamSchema: GenerateResponseChunkSchema, - }, - async (request, streamingCallback) => { - // Retrieve conversation history. - const history = await run( - 'retrieve-history', - request.conversationId, - async () => { - return (await historyStore?.load(request.conversationId)) || []; - } - ); - - // Run the user prompt (with history) through the primary LLM. - const mainResp = await ai.generate({ - prompt: request.prompt, - messages: history, - model: llms[request.llmIndex], - streamingCallback, - }); - - // Save history. - await run( - 'save-history', - { - conversationId: request.conversationId, - history: mainResp.messages, - }, - async () => { - await historyStore?.save(request.conversationId, mainResp.messages); - } - ); - return mainResp.text; - } -); - -ai.startFlowServer({ - flows: [chatbotFlow], -}); diff --git a/samples/chatbot/server/src/memory.ts b/samples/chatbot/server/src/memory.ts deleted file mode 100644 index 4c3561f01..000000000 --- a/samples/chatbot/server/src/memory.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2024 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 { MessageData } from 'genkit'; - -const chatHistory: Record = {}; - -export interface HistoryStore { - load(id: string): Promise; - save(id: string, history: MessageData[]): Promise; -} - -export function inMemoryStore(): HistoryStore { - return { - async load(id: string): Promise { - return chatHistory[id]; - }, - async save(id: string, history: MessageData[]) { - chatHistory[id] = history; - }, - }; -} diff --git a/samples/chatbot/server/tsconfig.json b/samples/chatbot/server/tsconfig.json deleted file mode 100644 index efbb566bf..000000000 --- a/samples/chatbot/server/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compileOnSave": true, - "include": ["src"], - "compilerOptions": { - "module": "commonjs", - "noImplicitReturns": true, - "outDir": "lib", - "sourceMap": true, - "strict": true, - "target": "es2017", - "skipLibCheck": true, - "esModuleInterop": true - } -} diff --git a/samples/js-angular/.gitignore b/samples/js-angular/.gitignore deleted file mode 100644 index 7951405f8..000000000 --- a/samples/js-angular/.gitignore +++ /dev/null @@ -1 +0,0 @@ -lib \ No newline at end of file diff --git a/samples/js-angular/README.md b/samples/js-angular/README.md deleted file mode 100644 index 4e02b6f32..000000000 --- a/samples/js-angular/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Angular and Genkit streaming sample - -This is a simple UI for streaming RPG character generator. - -To build: - -```bash -npm i -npm run build -``` - -The sample is using Vertex AI, so you'll need to auth: - -```bash -gcloud auth application-default login -``` - -To run the sample: - -```bash -npm start -``` - -Point your browser to http://localhost:4200/ diff --git a/samples/js-angular/genkit-app/.editorconfig b/samples/js-angular/genkit-app/.editorconfig deleted file mode 100644 index 59d9a3a3e..000000000 --- a/samples/js-angular/genkit-app/.editorconfig +++ /dev/null @@ -1,16 +0,0 @@ -# Editor configuration, see https://editorconfig.org -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 2 -insert_final_newline = true -trim_trailing_whitespace = true - -[*.ts] -quote_type = single - -[*.md] -max_line_length = off -trim_trailing_whitespace = false diff --git a/samples/js-angular/genkit-app/.gitignore b/samples/js-angular/genkit-app/.gitignore deleted file mode 100644 index cc7b14135..000000000 --- a/samples/js-angular/genkit-app/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. - -# Compiled output -/dist -/tmp -/out-tsc -/bazel-out - -# Node -/node_modules -npm-debug.log -yarn-error.log - -# IDEs and editors -.idea/ -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# Visual Studio Code -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.history/* - -# Miscellaneous -/.angular/cache -.sass-cache/ -/connect.lock -/coverage -/libpeerconnection.log -testem.log -/typings - -# System files -.DS_Store -Thumbs.db diff --git a/samples/js-angular/genkit-app/README.md b/samples/js-angular/genkit-app/README.md deleted file mode 100644 index 0aeb2095b..000000000 --- a/samples/js-angular/genkit-app/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# GenkitApp - -This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 18.0.2. - -## Development server - -Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. - -## Code scaffolding - -Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. - -## Build - -Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. - -## Running unit tests - -Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). - -## Running end-to-end tests - -Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. - -## Further help - -To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/samples/js-angular/genkit-app/angular.json b/samples/js-angular/genkit-app/angular.json deleted file mode 100644 index 8762ae48e..000000000 --- a/samples/js-angular/genkit-app/angular.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "newProjectRoot": "projects", - "projects": { - "genkit-app": { - "projectType": "application", - "schematics": { - "@schematics/angular:component": { - "style": "scss" - } - }, - "root": "", - "sourceRoot": "src", - "prefix": "app", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:application", - "options": { - "outputPath": "dist/genkit-app", - "index": "src/index.html", - "browser": "src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "tsconfig.app.json", - "inlineStyleLanguage": "scss", - "assets": [ - { - "glob": "**/*", - "input": "public" - } - ], - "styles": [ - "@angular/material/prebuilt-themes/azure-blue.css", - "src/styles.scss" - ], - "scripts": [] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kB", - "maximumError": "1MB" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kB", - "maximumError": "4kB" - } - ], - "outputHashing": "all" - }, - "development": { - "optimization": false, - "extractLicenses": false, - "sourceMap": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "builder": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "genkit-app:build:production" - }, - "development": { - "buildTarget": "genkit-app:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n" - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "polyfills": ["zone.js", "zone.js/testing"], - "tsConfig": "tsconfig.spec.json", - "inlineStyleLanguage": "scss", - "assets": [ - { - "glob": "**/*", - "input": "public" - } - ], - "styles": [ - "@angular/material/prebuilt-themes/azure-blue.css", - "src/styles.scss" - ], - "scripts": [] - } - } - } - } - } -} diff --git a/samples/js-angular/genkit-app/package.json b/samples/js-angular/genkit-app/package.json deleted file mode 100644 index 89cd74b7f..000000000 --- a/samples/js-angular/genkit-app/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "genkit-app", - "version": "0.0.0", - "scripts": { - "ng": "ng", - "start": "ng serve", - "build": "ng build", - "watch": "ng build --watch --configuration development", - "test": "ng test" - }, - "private": true, - "dependencies": { - "@angular/animations": "^18.0.0", - "@angular/cdk": "^18.0.1", - "@angular/common": "^18.0.0", - "@angular/compiler": "^18.0.0", - "@angular/core": "^18.0.0", - "@angular/forms": "^18.0.0", - "@angular/material": "^18.0.1", - "@angular/platform-browser": "^18.0.0", - "@angular/platform-browser-dynamic": "^18.0.0", - "@angular/router": "^18.0.0", - "rxjs": "~7.8.0", - "tslib": "^2.3.0", - "zone.js": "~0.14.3", - "genkit": "^0.9.0-rc || ^0.9" - }, - "devDependencies": { - "@angular-devkit/build-angular": "^18.0.2", - "@angular/cli": "^18.0.2", - "@angular/compiler-cli": "^18.0.0", - "@types/jasmine": "~5.1.0", - "jasmine-core": "~5.1.0", - "karma": "~6.4.0", - "karma-chrome-launcher": "~3.2.0", - "karma-coverage": "~2.2.0", - "karma-jasmine": "~5.1.0", - "karma-jasmine-html-reporter": "~2.1.0", - "typescript": "~5.4.2" - } -} diff --git a/samples/js-angular/genkit-app/public/favicon.ico b/samples/js-angular/genkit-app/public/favicon.ico deleted file mode 100644 index 57614f9c967596fad0a3989bec2b1deff33034f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 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 Nr__JTFzvMr^FKp4S3v*( diff --git a/samples/js-angular/genkit-app/src/app/app.component.html b/samples/js-angular/genkit-app/src/app/app.component.html deleted file mode 100644 index 0165f5b0f..000000000 --- a/samples/js-angular/genkit-app/src/app/app.component.html +++ /dev/null @@ -1,36 +0,0 @@ - - - diff --git a/samples/js-angular/genkit-app/src/app/app.component.scss b/samples/js-angular/genkit-app/src/app/app.component.scss deleted file mode 100644 index 0e0b80557..000000000 --- a/samples/js-angular/genkit-app/src/app/app.component.scss +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright 2024 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. - */ - -app-bar { - border-bottom: 1px solid var(--divider-color); - grid-area: header; -} - -article { - grid-area: content; -} - -.wrapper { - display: grid; - grid-template: - 'header' auto - 'content' 1fr - / 1fr; - height: 100vh; -} - -.home-link { - align-items: center; - color: var(--mat-app-color); - display: flex; - gap: 8px; - - img { - height: 22px; - padding-left: 4px; - } -} - -.mat-toolbar { - background: #d7e3ff; - color: #005cbb; - gap: 4px; -} - -nav { - --mdc-secondary-navigation-tab-container-height: 64px; - --mat-tab-header-divider-height: 0; - margin-left: 32px; -} - -.preview-badge { - margin-left: 8px; - - mat-icon { - font-size: 18px; - height: 18px; - width: 18px; - } -} diff --git a/samples/js-angular/genkit-app/src/app/app.component.spec.ts b/samples/js-angular/genkit-app/src/app/app.component.spec.ts deleted file mode 100644 index 02dac7e71..000000000 --- a/samples/js-angular/genkit-app/src/app/app.component.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2024 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 { TestBed } from '@angular/core/testing'; -import { AppComponent } from './app.component'; - -describe('AppComponent', () => { - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [AppComponent], - }).compileComponents(); - }); - - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); - - it(`should have the 'genkit-app' title`, () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app.title).toEqual('genkit-app'); - }); - - it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain( - 'Hello, genkit-app' - ); - }); -}); diff --git a/samples/js-angular/genkit-app/src/app/app.component.ts b/samples/js-angular/genkit-app/src/app/app.component.ts deleted file mode 100644 index 39d4f4ea0..000000000 --- a/samples/js-angular/genkit-app/src/app/app.component.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2024 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 { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; -import { MatButtonModule } from '@angular/material/button'; -import { MatIconModule } from '@angular/material/icon'; -import { MatTabNavPanel, MatTabsModule } from '@angular/material/tabs'; -import { MatToolbarModule } from '@angular/material/toolbar'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; - -@Component({ - selector: 'app-root', - standalone: true, - imports: [ - CommonModule, - MatToolbarModule, - RouterOutlet, - MatIconModule, - MatTabNavPanel, - MatButtonModule, - MatTabsModule, - MatToolbarModule, - MatTooltipModule, - RouterLink, - RouterLinkActive, - ], - templateUrl: './app.component.html', - styleUrl: './app.component.scss', -}) -export class AppComponent {} diff --git a/samples/js-angular/genkit-app/src/app/app.config.ts b/samples/js-angular/genkit-app/src/app/app.config.ts deleted file mode 100644 index 3d04dfa9c..000000000 --- a/samples/js-angular/genkit-app/src/app/app.config.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright 2024 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 { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; -import { provideRouter } from '@angular/router'; - -import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; -import { routes } from './app.routes'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideZoneChangeDetection({ eventCoalescing: true }), - provideRouter(routes), - provideAnimationsAsync(), - ], -}; diff --git a/samples/js-angular/genkit-app/src/app/app.routes.ts b/samples/js-angular/genkit-app/src/app/app.routes.ts deleted file mode 100644 index bd70f607f..000000000 --- a/samples/js-angular/genkit-app/src/app/app.routes.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2024 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 { Routes } from '@angular/router'; -import { HomeComponent } from './home/home.component'; -import { ChatbotComponent } from './samples/chatbot/chatbot.component'; -import { StreamingJSONComponent } from './samples/streaming-json/streaming-json.component'; - -export const routes: Routes = [ - { - path: 'home', - component: HomeComponent, - }, - { - path: 'samples/streaming-json', - component: StreamingJSONComponent, - }, - { - path: 'samples/chatbot', - component: ChatbotComponent, - }, - { path: '**', redirectTo: '/home' }, -]; diff --git a/samples/js-angular/genkit-app/src/app/home/home.component.html b/samples/js-angular/genkit-app/src/app/home/home.component.html deleted file mode 100644 index f57987dcc..000000000 --- a/samples/js-angular/genkit-app/src/app/home/home.component.html +++ /dev/null @@ -1,32 +0,0 @@ - - -
-

Samples

- - - -
diff --git a/samples/js-angular/genkit-app/src/app/home/home.component.scss b/samples/js-angular/genkit-app/src/app/home/home.component.scss deleted file mode 100644 index da80fa8b3..000000000 --- a/samples/js-angular/genkit-app/src/app/home/home.component.scss +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright 2024 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. - */ - -.wrapper { - padding: 20px; -} diff --git a/samples/js-angular/genkit-app/src/app/home/home.component.spec.ts b/samples/js-angular/genkit-app/src/app/home/home.component.spec.ts deleted file mode 100644 index 19eda49ae..000000000 --- a/samples/js-angular/genkit-app/src/app/home/home.component.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2024 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 { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { HomeComponent } from './home.component'; - -describe('HomeComponent', () => { - let component: HomeComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [HomeComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(HomeComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/samples/js-angular/genkit-app/src/app/home/home.component.ts b/samples/js-angular/genkit-app/src/app/home/home.component.ts deleted file mode 100644 index f1e1997c1..000000000 --- a/samples/js-angular/genkit-app/src/app/home/home.component.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright 2024 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 { Component } from '@angular/core'; -import { RouterLink, RouterLinkActive } from '@angular/router'; - -@Component({ - selector: 'app-home', - standalone: true, - imports: [RouterLink, RouterLinkActive], - templateUrl: './home.component.html', - styleUrl: './home.component.scss', -}) -export class HomeComponent {} diff --git a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.html b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.html deleted file mode 100644 index 00829ec6d..000000000 --- a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.html +++ /dev/null @@ -1,77 +0,0 @@ - - -
-

Chat with Agent Smith

- -
-
- {{ entry.text }} -
-
- {{ entry.text }} -
-
-
-

- sunny_snowing - {{ getWeatherLocation(entry.toolRequest) }} 27°C -

-
Warn sunny day with a mix of sun and snow.
-
-
- - Choose a date - - MM/DD/YYYY - - - - - - - - -
-
- Oops... unknown tool {{ entry.toolRequest.name }} -
-
-
-
-
- -
- - Chat input - - - - -
-
diff --git a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.scss b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.scss deleted file mode 100644 index da62c1c12..000000000 --- a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.scss +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2024 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. - */ - -.wrapper { - margin-left: auto; - margin-right: auto; - padding: 20px; - width: 800px; -} - -.user-bubble { - background-color: #eeddee; - border: 1px solid #ccc; - border-radius: 10px; - margin-bottom: 20px; - margin-left: auto; - margin-right: 0; - min-width: 300px; - padding: 20px; - white-space: pre-wrap; - width: 80%; -} - -.model-bubble { - background-color: #ddddee; - border: 1px solid #ccc; - border-radius: 10px; - margin-bottom: 20px; - min-width: 300px; - padding: 20px; - width: 80%; - - .text { - white-space: pre-wrap; - } -} - -.input-field { - min-width: 400px; - vertical-align: top; - width: 730px; -} diff --git a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts deleted file mode 100644 index c79a6e1f7..000000000 --- a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2024 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 { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ChatbotComponent } from './chatbot.component'; - -describe('ChatbotComponent', () => { - let component: ChatbotComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ChatbotComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(ChatbotComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts deleted file mode 100644 index 98e454219..000000000 --- a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Copyright 2024 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 { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; -import { - FormControl, - FormsModule, - ReactiveFormsModule, - Validators, -} from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { provideNativeDateAdapter } from '@angular/material/core'; -import { - MatDatepickerInputEvent, - MatDatepickerModule, -} from '@angular/material/datepicker'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatIconModule } from '@angular/material/icon'; -import { MatInputModule } from '@angular/material/input'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { streamFlow } from 'genkit/client'; - -const url = 'http://127.0.0.1:3400/chatbotFlow'; - -interface ToolResponse { - name: string; - ref: string; - output?: unknown; -} - -interface InputSchema { - role: 'user'; - text?: string; - toolResponse?: ToolResponse; -} - -interface ToolRequest { - name: string; - ref: string; - input?: unknown; -} -interface OutputSchema { - role: 'model'; - text?: string; - toolRequest?: ToolRequest; -} - -@Component({ - selector: 'app-chatbot', - standalone: true, - providers: [provideNativeDateAdapter()], - imports: [ - CommonModule, - FormsModule, - MatFormFieldModule, - MatInputModule, - ReactiveFormsModule, - MatButtonModule, - MatIconModule, - MatProgressBarModule, - MatDatepickerModule, - ], - templateUrl: './chatbot.component.html', - styleUrl: './chatbot.component.scss', -}) -export class ChatbotComponent { - history: (InputSchema | OutputSchema)[] = []; - error?: string; - input?: string; - loading = false; - id = Date.now() + '' + Math.floor(Math.random() * 1000000000); - - chatFormControl = new FormControl('', [Validators.required]); - - ask(input?: string) { - const text = this.chatFormControl.value!.trim(); - if (!text) return; - this.history.push({ role: 'user', text: text }); - this.chatFormControl.setValue(''); - this.chatFormControl.disable(); - this.callFlow({ role: 'user', text }); - this.loading = true; - } - - async callFlow(input: InputSchema) { - this.error = undefined; - this.loading = true; - try { - const response = await streamFlow({ - url, - input: { - prompt: input, - conversationId: this.id, - }, - }); - - let textBlock: OutputSchema | undefined = undefined; - for await (const chunk of response.stream()) { - for (const content of chunk.content) { - if (content.text) { - if (!textBlock) { - textBlock = { role: 'model', text: content.text! }; - this.history.push(textBlock); - } else { - textBlock.text += content.text!; - } - } - if (content.toolRequest) { - this.history.push({ - role: 'model', - toolRequest: content.toolRequest, - }); - } - } - } - - this.loading = false; - this.chatFormControl.enable(); - } catch (e) { - this.loading = false; - this.chatFormControl.enable(); - if ((e as any).cause) { - this.error = `${(e as any).cause}`; - } else { - this.error = `${e}`; - } - } - } - - getWeatherLocation(toolRequest: ToolRequest) { - return (toolRequest.input as any).location; - } - - datePicked(toolRequest: ToolRequest, event: MatDatepickerInputEvent) { - this.callFlow({ - role: 'user', - toolResponse: { - name: toolRequest.name, - ref: toolRequest.ref, - output: `${event.value}`, - }, - }); - } -} diff --git a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.html b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.html deleted file mode 100644 index e32ce2ae0..000000000 --- a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.html +++ /dev/null @@ -1,36 +0,0 @@ - - -
-

Stream JSON from LLM

- This is a Game Character Generator.
- How many game chatacters do you need? - - - -
Loading...
-
- {{ error }} -
-
-
- {{ character.name }} -
    -
  • {{ ability }}
  • -
-
-
-
diff --git a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.scss b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.scss deleted file mode 100644 index 70fcce28e..000000000 --- a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright 2024 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. - */ - -.wrapper { - padding: 20px; -} - -.characters { - margin-top: 20px; -} diff --git a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.spec.ts b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.spec.ts deleted file mode 100644 index d85373105..000000000 --- a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2024 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 { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { StreamingJSONComponent } from './streaming-json.component'; - -describe('StreamingJSONComponent', () => { - let component: StreamingJSONComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [StreamingJSONComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(StreamingJSONComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts deleted file mode 100644 index 9d39d3148..000000000 --- a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2024 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 { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { streamFlow } from 'genkit/client'; - -const url = 'http://127.0.0.1:3400/streamCharacters'; - -@Component({ - selector: 'app-streaming-json', - standalone: true, - imports: [FormsModule, CommonModule, MatButtonModule], - templateUrl: './streaming-json.component.html', - styleUrl: './streaming-json.component.scss', -}) -export class StreamingJSONComponent { - count: string = '3'; - characters: any = undefined; - error?: string = undefined; - loading: boolean = false; - - async callFlow() { - this.characters = undefined; - this.error = undefined; - this.loading = true; - try { - const response = streamFlow({ - url, - input: parseInt(this.count), - }); - for await (const chunk of response.stream()) { - this.characters = chunk; - } - console.log('streamConsumer done', await response.output()); - this.loading = false; - } catch (e) { - this.loading = false; - if ((e as any).cause) { - this.error = `${(e as any).cause}`; - } else { - this.error = `${e}`; - } - } - } -} diff --git a/samples/js-angular/genkit-app/src/index.html b/samples/js-angular/genkit-app/src/index.html deleted file mode 100644 index 822a28173..000000000 --- a/samples/js-angular/genkit-app/src/index.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - GenkitApp - - - - - - - - - - diff --git a/samples/js-angular/genkit-app/src/main.ts b/samples/js-angular/genkit-app/src/main.ts deleted file mode 100644 index b1be530a2..000000000 --- a/samples/js-angular/genkit-app/src/main.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright 2024 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 { bootstrapApplication } from '@angular/platform-browser'; -import { AppComponent } from './app/app.component'; -import { appConfig } from './app/app.config'; - -bootstrapApplication(AppComponent, appConfig).catch((err) => - console.error(err) -); diff --git a/samples/js-angular/genkit-app/src/styles.scss b/samples/js-angular/genkit-app/src/styles.scss deleted file mode 100644 index 0ff0b59e1..000000000 --- a/samples/js-angular/genkit-app/src/styles.scss +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2024 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. - */ - -/* You can add global styles to this file, and also import other style files */ - -:root { - --header-height: 65px; - --container-border-radius: 20px; - --input-border-radius: 8px; -} - -html, -body { - height: 100%; -} - -body { - background-color: var(--app-background); - color: var(--mat-app-text-color); - margin: 0; -} - -hr { - border-bottom: 1px solid var(--divider-color); - border-width: 0 0 1px; - margin: 12px 0; -} - -a { - color: var(--link-color); - text-decoration: none; -} - -pre { - margin: 0; - white-space: pre-wrap; -} - -// Helper for filling available space in flex layouts -.flex-spacer { - flex: 1; -} diff --git a/samples/js-angular/genkit-app/tsconfig.app.json b/samples/js-angular/genkit-app/tsconfig.app.json deleted file mode 100644 index 84f1f992d..000000000 --- a/samples/js-angular/genkit-app/tsconfig.app.json +++ /dev/null @@ -1,10 +0,0 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "./out-tsc/app", - "types": [] - }, - "files": ["src/main.ts"], - "include": ["src/**/*.d.ts"] -} diff --git a/samples/js-angular/genkit-app/tsconfig.json b/samples/js-angular/genkit-app/tsconfig.json deleted file mode 100644 index 437984834..000000000 --- a/samples/js-angular/genkit-app/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ -{ - "compileOnSave": false, - "compilerOptions": { - "outDir": "./dist/out-tsc", - "strict": true, - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "skipLibCheck": true, - "esModuleInterop": true, - "sourceMap": true, - "declaration": false, - "experimentalDecorators": true, - "moduleResolution": "bundler", - "importHelpers": true, - "target": "ES2022", - "module": "ES2022", - "useDefineForClassFields": false, - "lib": ["ES2022", "dom"] - }, - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true - } -} diff --git a/samples/js-angular/genkit-app/tsconfig.spec.json b/samples/js-angular/genkit-app/tsconfig.spec.json deleted file mode 100644 index 47e3dd755..000000000 --- a/samples/js-angular/genkit-app/tsconfig.spec.json +++ /dev/null @@ -1,9 +0,0 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "./out-tsc/spec", - "types": ["jasmine"] - }, - "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] -} diff --git a/samples/js-angular/package.json b/samples/js-angular/package.json deleted file mode 100644 index 07f36dae4..000000000 --- a/samples/js-angular/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "js-angular", - "version": "1.0.0", - "description": "This is a simple UI for streaming RPG character generator.", - "scripts": { - "start": "concurrently npm:start:server npm:start:ng", - "start:server": "cd server && npm run genkit:dev", - "start:ng": "cd genkit-app && npm run start", - "setup": "npm i && cd server && npm i && cd ../genkit-app && npm i" - }, - "keywords": [], - "author": "", - "license": "ISC", - "devDependencies": { - "concurrently": "^8.2.2" - } -} diff --git a/samples/js-angular/server/package.json b/samples/js-angular/server/package.json deleted file mode 100644 index 428c00d1c..000000000 --- a/samples/js-angular/server/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "main": "lib/index.js", - "scripts": { - "start": "node lib/index.js", - "genkit:dev": "genkit start -- tsx --watch src/index.ts", - "build": "tsc", - "build:watch": "tsc --watch", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "name": "js-angular", - "version": "1.0.0", - "description": "This is a simple UI for streaming RPG character generator.", - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "genkit": "^0.9.0-rc || ^0.9", - "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9", - "express": "^4.21.0", - "partial-json": "^0.1.7" - }, - "devDependencies": { - "genkit-cli": "^0.9.0-rc || ^0.9", - "typescript": "^5.4.5", - "tsx": "^4.19.2" - } -} diff --git a/samples/js-angular/server/src/agent.ts b/samples/js-angular/server/src/agent.ts deleted file mode 100644 index 3ee2f03b5..000000000 --- a/samples/js-angular/server/src/agent.ts +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright 2024 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 { - GenerateResponse, - Genkit, - MessageData, - ModelArgument, - PartSchema, - ToolArgument, - run, - z, -} from 'genkit'; -import { GenerateResponseSchema } from 'genkit/model'; - -export interface HistoryStore { - load(id: string): Promise; - save(id: string, history: MessageData[]): Promise; -} - -export const AgentInput = z.object({ - conversationId: z.string(), - prompt: z.union([z.string(), PartSchema, z.array(PartSchema)]), - config: z.record(z.string(), z.any()).optional(), -}); - -type AgentFn = ( - request: z.infer, - history: MessageData[] | undefined -) => Promise>; - -export function defineAgent( - ai: Genkit, - { - name, - tools, - model, - historyStore, - systemPrompt, - returnToolRequests, - }: { - name: string; - systemPrompt?: string; - tools?: ToolArgument[]; - model: ModelArgument; - historyStore?: HistoryStore; - returnToolRequests?: boolean; - }, - customFn?: AgentFn -) { - return ai.defineFlow( - { name, inputSchema: AgentInput, outputSchema: GenerateResponseSchema }, - async (request, streamingCallback) => { - const history = await run( - 'retrieve-history', - request.conversationId, - async () => { - let history = request.conversationId - ? await historyStore?.load(request.conversationId) - : undefined; - if (!history && systemPrompt) { - history = [ - { - role: 'system', - content: [ - { - text: systemPrompt, - }, - ], - }, - ]; - } - return history; - } - ); - const resp = customFn - ? await customFn(request, history) - : await ai.generate({ - prompt: request.prompt, - messages: history, - model, - tools, - returnToolRequests, - streamingCallback, - }); - await run( - 'save-history', - { conversationId: request.conversationId, history: resp.messages }, - async () => { - request.conversationId - ? await historyStore?.save(request.conversationId, resp.messages) - : undefined; - } - ); - return resp.toJSON(); - } - ); -} diff --git a/samples/js-angular/server/src/chatbot.ts b/samples/js-angular/server/src/chatbot.ts deleted file mode 100644 index 14f661406..000000000 --- a/samples/js-angular/server/src/chatbot.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright 2024 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 { MessageData } from '@genkit-ai/ai/model'; -import { gemini15Flash } from '@genkit-ai/vertexai'; -import { z } from 'zod'; -import { defineAgent, HistoryStore } from './agent.js'; -import { ai } from './genkit.js'; - -const weatherTool = ai.defineTool( - { - name: 'weatherTool', - description: 'use this tool to display weather', - inputSchema: z.object({ - date: z - .string() - .describe('date (use datePicker tool if user did not specify)'), - location: z.string().describe('location (ZIP, city, etc.)'), - }), - outputSchema: z.string().optional(), - }, - async () => undefined -); - -const datePicker = ai.defineTool( - { - name: 'datePicker', - description: - 'user can use this UI tool to enter a date (prefer this over asking the user to enter the date manually)', - inputSchema: z.object({ - ignore: z.string().describe('ignore this (set to undefined)').optional(), - }), - outputSchema: z.string().optional(), - }, - async () => undefined -); - -export const chatbotFlow = defineAgent(ai, { - name: 'chatbotFlow', - model: gemini15Flash, - tools: [weatherTool, datePicker], - returnToolRequests: true, - systemPrompt: - 'You are a helpful agent. You have the personality of Agent Smith from Matrix. ' + - 'There are tools/functions at your disposal, ' + - 'feel free to call them. If you think a tool/function can help but you do ' + - 'not have sufficient context make sure to ask clarifying questions.', - historyStore: inMemoryStore(), -}); - -const chatHistory: Record = {}; - -function inMemoryStore(): HistoryStore { - return { - async load(id: string): Promise { - return chatHistory[id]; - }, - async save(id: string, history: MessageData[]) { - chatHistory[id] = history; - }, - }; -} diff --git a/samples/js-angular/server/src/genkit.ts b/samples/js-angular/server/src/genkit.ts deleted file mode 100644 index 5da87fb17..000000000 --- a/samples/js-angular/server/src/genkit.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright 2024 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 vertexAI from '@genkit-ai/vertexai'; -import { genkit } from 'genkit'; - -export const ai = genkit({ - plugins: [vertexAI()], -}); diff --git a/samples/js-angular/server/src/index.ts b/samples/js-angular/server/src/index.ts deleted file mode 100644 index e5b080060..000000000 --- a/samples/js-angular/server/src/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright 2024 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 { chatbotFlow } from './chatbot.js'; -import { ai } from './genkit.js'; -import { streamCharacters } from './jsonStreaming.js'; - -ai.startFlowServer({ - flows: [chatbotFlow, streamCharacters], -}); diff --git a/samples/js-angular/server/src/jsonStreaming.ts b/samples/js-angular/server/src/jsonStreaming.ts deleted file mode 100644 index df0611100..000000000 --- a/samples/js-angular/server/src/jsonStreaming.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright 2024 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 { gemini15Flash } from '@genkit-ai/vertexai'; -import { z } from 'genkit'; -import { Allow, parse } from 'partial-json'; -import { ai } from './genkit.js'; - -const GameCharactersSchema = z.object({ - characters: z - .array( - z - .object({ - name: z.string().describe('Name of a character'), - abilities: z - .array(z.string()) - .describe('Various abilities (strength, magic, archery, etc.)'), - }) - .describe('Game character') - ) - .describe('Characters'), -}); - -export const streamCharacters = ai.defineStreamingFlow( - { - name: 'streamCharacters', - inputSchema: z.number(), - outputSchema: z.string(), - streamSchema: GameCharactersSchema, - }, - async (count, streamingCallback) => { - if (!streamingCallback) { - throw new Error('this flow only works in streaming mode'); - } - - const { response, stream } = await ai.generateStream({ - model: gemini15Flash, - output: { - format: 'json', - schema: GameCharactersSchema, - }, - config: { - temperature: 1, - }, - prompt: `Respond as JSON only. Generate ${count} different RPG game characters.`, - }); - - let buffer = ''; - for await (const chunk of stream) { - buffer += chunk.content[0].text!; - if (buffer.length > 10) { - streamingCallback(parse(maybeStripMarkdown(buffer), Allow.ALL)); - } - } - - return (await response).text; - } -); - -const markdownRegex = /^\s*(```json)?((.|\n)*?)(```)?\s*$/i; -function maybeStripMarkdown(withMarkdown: string) { - const mdMatch = markdownRegex.exec(withMarkdown); - if (!mdMatch) { - return withMarkdown; - } - return mdMatch[2]; -} diff --git a/samples/js-angular/server/tsconfig.json b/samples/js-angular/server/tsconfig.json deleted file mode 100644 index efbb566bf..000000000 --- a/samples/js-angular/server/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compileOnSave": true, - "include": ["src"], - "compilerOptions": { - "module": "commonjs", - "noImplicitReturns": true, - "outDir": "lib", - "sourceMap": true, - "strict": true, - "target": "es2017", - "skipLibCheck": true, - "esModuleInterop": true - } -} diff --git a/samples/js-coffee-shop/README.md b/samples/js-coffee-shop/README.md deleted file mode 100644 index 618a80f5e..000000000 --- a/samples/js-coffee-shop/README.md +++ /dev/null @@ -1,6 +0,0 @@ -## Running the sample - -```bash -npm i -genkit start -``` diff --git a/samples/js-coffee-shop/package.json b/samples/js-coffee-shop/package.json deleted file mode 100644 index b32e5b1b5..000000000 --- a/samples/js-coffee-shop/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "coffee-shop", - "version": "1.0.0", - "description": "Genkit samples for a coffeeshop", - "main": "lib/index.js", - "scripts": { - "start": "node lib/index.js", - "genkit:dev": "genkit start -- tsx --watch src/index.ts", - "compile": "tsc", - "build": "npm run build:clean && npm run compile", - "build:clean": "rimraf ./lib", - "build:watch": "tsc --watch", - "build-and-run": "npm run build && node lib/index.js" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "genkit": "^0.9.0-rc || ^0.9", - "genkitx-chromadb": "^0.9.0-rc || ^0.9", - "@genkit-ai/dev-local-vectorstore": "^0.9.0-rc || ^0.9", - "@genkit-ai/firebase": "^0.9.0-rc || ^0.9", - "@genkit-ai/googleai": "^0.9.0-rc || ^0.9", - "genkitx-ollama": "^0.9.0-rc || ^0.9", - "genkitx-pinecone": "^0.9.0-rc || ^0.9", - "@genkit-ai/evaluator": "^0.9.0-rc || ^0.9", - "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9", - "zod": "^3.22.4" - }, - "devDependencies": { - "genkit-cli": "^0.9.0-rc || ^0.9", - "rimraf": "^6.0.1", - "typescript": "^5.3.3" - } -} diff --git a/samples/js-coffee-shop/src/index.ts b/samples/js-coffee-shop/src/index.ts deleted file mode 100644 index 5e6745b12..000000000 --- a/samples/js-coffee-shop/src/index.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright 2024 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 { gemini15Flash, googleAI } from '@genkit-ai/googleai'; -import { genkit, z } from 'genkit'; - -const ai = genkit({ - plugins: [googleAI()], -}); - -// This example generates greetings for a customer at our new AI-powered coffee shop, -// demonstrating how to use prompts in Genkit flows. - -// A flow to greet a customer by name - -const CustomerNameSchema = z.object({ - customerName: z.string(), -}); - -const simpleGreetingPrompt = ai.definePrompt( - { - name: 'simpleGreeting', - model: gemini15Flash, - input: { schema: CustomerNameSchema }, - output: { - format: 'text', - }, - }, - ` -You're a barista at a nice coffee shop. -A regular customer named {{customerName}} enters. -Greet the customer in one sentence, and recommend a coffee drink. -` -); - -export const simpleGreetingFlow = ai.defineFlow( - { - name: 'simpleGreeting', - inputSchema: CustomerNameSchema, - outputSchema: z.string(), - }, - async (input) => (await simpleGreetingPrompt(input)).text -); - -// Another flow to recommend a drink based on the time of day and a previous order. -// This prompt uses multiple messages, alternating roles -// to make the response more conversational. - -const CustomerTimeAndHistorySchema = z.object({ - customerName: z.string(), - currentTime: z.string(), - previousOrder: z.string(), -}); - -const greetingWithHistoryPrompt = ai.definePrompt( - { - name: 'greetingWithHistory', - model: gemini15Flash, - input: { schema: CustomerTimeAndHistorySchema }, - output: { - format: 'text', - }, - }, - ` -{{role "user"}} -Hi, my name is {{customerName}}. The time is {{currentTime}}. Who are you? - -{{role "model"}} -I am Barb, a barista at this nice underwater-themed coffee shop called Krabby Kooffee. -I know pretty much everything there is to know about coffee, -and I can cheerfully recommend delicious coffee drinks to you based on whatever you like. - -{{role "user"}} -Great. Last time I had {{previousOrder}}. -I want you to greet me in one sentence, and recommend a drink. -` -); - -export const greetingWithHistoryFlow = ai.defineFlow( - { - name: 'greetingWithHistory', - inputSchema: CustomerTimeAndHistorySchema, - outputSchema: z.string(), - }, - async (input) => (await greetingWithHistoryPrompt(input)).text -); - -// A flow to quickly test all the above flows -// Run on the CLI with `$ genkit flow:run testAllCoffeeFlows` -// View the trace in the Developer UI to see the llm responses. - -export const testAllCoffeeFlows = ai.defineFlow( - { - name: 'testAllCoffeeFlows', - outputSchema: z.object({ - pass: z.boolean(), - error: z.string().optional(), - }), - }, - async () => { - const test1 = simpleGreetingFlow({ customerName: 'Sam' }); - const test2 = greetingWithHistoryFlow({ - customerName: 'Sam', - currentTime: '09:45am', - previousOrder: 'Caramel Macchiato', - }); - - return Promise.all([test1, test2]) - .then((unused) => { - return { pass: true }; - }) - .catch((e: Error) => { - return { pass: false, error: e.toString() }; - }); - } -); diff --git a/samples/js-coffee-shop/src/input.json b/samples/js-coffee-shop/src/input.json deleted file mode 100644 index d669609ad..000000000 --- a/samples/js-coffee-shop/src/input.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "start": { - "customerName": "sam", - "currentTime": "12:30AM", - "previousOrders": ["Americano"] - } -} diff --git a/samples/js-coffee-shop/tsconfig.json b/samples/js-coffee-shop/tsconfig.json deleted file mode 100644 index e51f33ae3..000000000 --- a/samples/js-coffee-shop/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "module": "NodeNext", - "noImplicitReturns": true, - "noUnusedLocals": false, - "outDir": "lib", - "sourceMap": true, - "strict": true, - "target": "es2017", - "skipLibCheck": true, - "esModuleInterop": true - }, - "compileOnSave": true, - "include": ["src"] -} diff --git a/samples/js-menu/README.md b/samples/js-menu/README.md deleted file mode 100644 index 8200d81b6..000000000 --- a/samples/js-menu/README.md +++ /dev/null @@ -1,27 +0,0 @@ -## Menu Understanding Sample Application - -This sample demonstrates an application that can understand a restaurant menu and answer relevant questions about the items on the menu. - -There are 5 iterations of this sample application, growing in complexity and demonstrating utilization of many different Genkit features. - -To test each one out, open the Developer UI and exercise the prompts and flows. Each step contains one or more `example.json` files which you can use as inputs. - -### Prerequisites - -This example uses Vertex AI for language models and embeddings. - -### Prompts and Flows - -1. This step shows how to define prompts in code that can accept user input to their templates. -2. This step illustrates how to wrap your llm calls and other application code into flows with strong input and output schemas. - It also adds an example of tool usage to load the menu from a data file. -3. This step adds session history and supports a multi-turn chat with the model. -4. This step ingests the menu items into a vector database and uses retrieval to include releveant menu items in the prompt. -5. This step illustrates how to combine models with different modalities. It uses a vision model to ingest the menu items from a photograph. - -## Running the sample - -```bash -npm i -genkit start -``` diff --git a/samples/js-menu/data/menu.jpeg b/samples/js-menu/data/menu.jpeg deleted file mode 100644 index ae096cd7bd8ddf5c48308bdc1c389d1efa8eb0e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 493635 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

`T&LsxeY-7b?=ciX_rNRvmQY`*>?QGwMb7r&6Gq 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

?3@97}wvLdA{sHoI-!Uq( zs{QydS`&Wch;WhKkz%Qu%VRqWo3EFxEUzrVB&Cx$4Awxt`UfZo?XCAt3VXn=NesEI zStZqY;0zb~Tew}sOn5UpRu5@l3;OkhZvixU=$(y3D`sBc}f06xG#~rxdt^ zkn=u#N{GFtJ-*ZaQdx{vRW~b+!ClBAkd#wbA+94V5qn-p6@;fm)m@G)>M)Z23cXvo zBz}f_&+SK71Kc5zlz{^|A|$Wr5Uu?@;#+-kTCE{ZDn{x3+=&0^TV%sB7FEBmHS9vX z1M^xFDd!RC`k5mJB%LK;h^y(~%b&q3J4x%s=dsrP)&)TyOtg@DL)M0LWNHVOcY&DhZAB10XILS=&U5>k-jsVm2-N|htLQ@mE`1QBs!h`ML zX&VAc3_mxOny$4W*C!rgg=fqmq6<-#?W}|vTt|558?6kLNijs*`1s)^X#5iwl1fq& z=1jxdGTBoh$GesHGkPI8feAop(vNS$cUo52P6XtepS{7}xJmt_Q+|S1K1{j{NdHGeqiBYk3c;5UU)EXm7=h{gS%z z9KgMwI~nz7d5*&!`mjIpA$wg%_qwp(L||#bqa6?40@XGV4KfeQAPkifWRHx&6+J!23ViFwLP)q;w$BL=U87@q!Or-@zzB#TkRNw_$s?e2&1 z=HNoWkNl*Zm;dQP`zlqRdF|yR#6flXb>;*`*HZj10-P=mXZKu#6}o=_9yX8OeWfd= zE7p4qRB|SA)QF&pwAsqMvamLSD?<*K%;b;Cw-jV~#NyJ64jg zKY8xUiI;WmvHZTVIyI3kwOe!J1*2!5J-fhbM(;=V(AUnX(BecMoc;5bxyJ2_&hQ?S z*0W5W`HXlp_-Q8cfbWw^R-D;6G2;IS91|a_+B1;G0fPn*IAocJI~EaF`7P1 zxW{DEEb+go0-_s^5Y9_XkLqir;Eq0|1xg_T6a()}Ci)kb|-IC9b?-avb^a9@ERR+2u1 zL~l7+y1H#V^QzPSA0V_+CR*T9fPv?H`G8Z8(5<#FCow_GBhi|fT2hrQ!S{|J#~4=z z0-U4u2oBM{6)6E{Vu0CByKbm%rIO0n4~eQ!lv!eF0M|d(I>#bG?op2|Z2{4QJ!t z*B;R9_NycZ%O(#@QY48@GEs1g?!vR#F|t2;O#1pLu>clE(z}2}paYoVx4RsQo;rKa zM6|Wl2aZJBI=s>3)es++(rWnZL75J!;DTQsTiJapAL@`4i@%YGbigK zytd2|jN*MsW1ICQmFGRN&zo@T6FTIc38gSeJT^bsaU^MfLPggrjOfqyR3uRY0EZ8y z0?uTp)bg^4Zy>$j>?*(zv{zWajZp)veaMqwP=U71>$D#Y!iUh!px3!?eY(vB@QyK?&_pPsAF2^6OJ^w+M{O1UF6(;9-bR-4MNOyT5Jro!!&l~C01-F( z;$CGV6;}LM!$9H%Pf&WEtnWAs^Ks$dx93rf;+kA)`oP?>%<3NKgq)M6GvQo75=x$? zEjaT>vDj2I!W*TC^`UlltX(7g?XU0gqncjAHQ9q@w#kGRCzUes#NI#E)HDF50=n8MOTK5roFI#}l_KZIzQ ze+q=iK92rvsGIL*tZM5jpSQmu{@TWOOP4HV`-3=pV)>wdnmKvmXXB#=E*ZMw`E(*e z>2FD)Z2MpD-v$_-Cgia6gq>+QC@*L>18l4^=v+#ZH zJ2da{H1sbTs6rAMI2>DW#qZcKtx5p$CPv}6N}hE-XlYSIcMI}4nv&sNDI!q6z|G{P z20B7ogTnHF9of#SqRr3O(qt5^P2xrZZQ!*SFgnMjAJ&;DT3NFytNrmWQj9CZ##{Ox zT7%Ck0~>qS-X+O@__lbSRWw`#rjC>EM;co)+1ls@k}n0tF_^whka;J6t)U+3 z3F5=i>sqw$gj+_31TOG7$yV3o#$>I}TdzheM^{C528b1WmB29PTS-dX4H_aIl70P& z5v{}gE)df*S!MUSU=Av=DV>)9c2+j%sA`E48y$piYi^HSweLIyMyX-#gZjukq}3vo zCE?Ktd6DHR76W)Pu;YJ#;L1p`87{coOu+%C)*5d(QJ%87&I__Ut^FWY1{Tf{52%&S zYbD`W&+^%e-Fzc;F|!`{l}-i+@&SKiUmk3ph<|8M+Z%xr(^JsQ1y!_ESAo6EW#%)2 z-mWrZ1L;psSY&Lf=&U2xJTh?tf0TcX*KmV`+0cc_z*{pfEr#K5hrLYIO!s^0LZd?h z7WfGDScILnbF<~DC!-ZD&olKs0*J;siyoq@4tQr=epm-zK%rR0sphsCWBvW<%YLFdNFQ3pr&8OWfxHOaTh#$LU}VlUqia3;p2N!9DH1C zFFy=b#NbuWKn}xEpftWo(fV@fR4^5o+8hc&1D?8iRk4mT_wpY4LdC9bKunKytt zqz6HhRVG_7g;-7a;tAIv%Zkh}pO_$sK91(8)i3rGP;aMf*{12OZqoN9g|u4?gB3ZV zbUf8738@h*jvMT=Zf49Cz?9MUy%NOJ1l8v!668STRpuExl$Q?M@90_v&wSfXMG|-a zCBi$JVc~9(LO9WY_)3e2h%}}@?1b^RCHjQGYM}Gi)%Zl)gl)K197}uWvK;@iRdp#Y z4S6k9_O4+LeP33(7NE_Tu8r#1Gbz zrHsP@t-O_h{6)9Y!}MNl2}i-hlqO7K5S!!4shkzN z-X*E{F2pmf$ykn|>U}YQ?&B)fAYi_;vJRXb?Ha&TiHYW54Dluz>wAH=ryBu%sF+-O zG>eme^`OOYX^^>hgS$Swy35T+>1g}Y9KsXf6U&sW2r&|&X5D15R4r^!EOkI1UZ$FI z9&;k%iSa?{Ak@O@3l3!^oy$yhkU_V=x%5wUTuyZuQaAR`MkARc^L1Tp+MmHV7te<^ z^q}y#dfaiGy1g3w>&(?bTC%C(Ao2<3!)W-mH`Q(-POM;}*(`cqtC9eXIC z<_~VAM%lx__6pf~IqElC-*H%~8TC9L%Mz`61BBzHb77Yxw)Lu6nzHL0^Q?F1wP85T zc9=>&9z{Ms+aierc)})AfT?JLc09hup%_u^@0R7BNcA8tQ1k^8u*H;9RlhdRFT3E_&-Z+42dK7yKa1K zfZ9XArxvy2itDGC&bDDXXhfJqX?6=oKt;1)Xl@Wn`oSUun`Y%=Yr)n2 zx;{j?Pe^huxdQYjRM3ndDIICk;+SJxFrcGLD6mD&urR&uFlE9?!-)U{G_i4#H}z0t zkS5qzxSOBdjpJZy+R^Z96QA{Ct0~Qyr=t;)^oOy4UB9EP&*1+0@U<{6KPgWBr+~E&lhLk6lA@vY#xG z8UF!HIbO|iJ9X5jP!AdX1FX7*%6)>fz0eioH)4e@@2V*%a+|Y=1c8XIP)0VBlQkPs z>zxx8n}0->?kAL$U|5s!!S7fCj0XK8)hioj;rMtG<>K>KRzFb|hQ=i2C`iI_-SH$J zBa8k4USQ$o%Vb7DM`t7s8(rC47tk}nS6PQE*;mgjd& zwPsBaSyCCw$y$(@&}U8NLyl+roLGU0{3oW1-|SrrAENHD!RS7xKdrI^1<>+-b%tF` zt+p`N&|~RTL=rh2nv^qfd`#m`o(#z3)G-q0nP!mM=pqVLagF^T<5h2~Xjky3SngU& zr#J4Q+=u|jwos`{M;4kL8SS^{ow+dkAVS+5oIHLUsZ&8q?2(o|6aif?JiVhIMf zzp#)3)j_Ft#MGaLFK@Bac$-+EOOa!dtP9vx zAt0}G8Q4M@IOKXOClA2weIIe*RDo~+5fEFMtPrXp>}n1bOMT9nl4^{UF&(-khS=;V z{p9#-B}x`fZeg{kD9~_3{XK3tBk4YlniD#2IP(!K`!lSjgBd_Ss&4hiHdQY`<&&i- z&6K`(EMBER{H=yI+xYG}wm@FmXbwiri@=c|iLl%^T`i0GdTu#FWsP1XfG=}c?9?w8 z45xg2G0=MO`fG7*&wbZg zHU@tBP+1`DwN|kb&PYr^qmlE_&sDX9p0PiuyV*=6%HlZ7BQ7`6O!F`Sb&lmTA*u-G znbhs_aDiz5Ht_(50Ga3cFLlC(NgKviau0b}g_P{VI>==fop1Iwr!p9^AN%;qQhKAV zfj>^;fJN2jxKTOCA3cE&w7;CIj?tyU>ZX&~q&oGmO60MSsL^La4o(k2S+lcbQ--U| z{Y($M%KD9SyEc5Cg{j+N+?O!O!?J9?0Rw6fsv;t1w&cJ|e7!V~zSJn9RG;UoHg9xM#D~vxNh|}9^c!8Q_6caC zBVQd{b6zZ5MZZpcGSEvsoZ^tutvtYP+PnW&F7cx3vdBr#@TsHikR7k%*GksTkWMnD z$3iZ?-tE37Wig$8b2vIz4D&MF_#kn?3t{wT>8d1$iJXq%m*{S8pAPci<0EJ>4yr82 zj~>GoOPWn77p1-kvAMu9M-h8hu_~yPYIvKhMX5Kn>&4(?-*W*tsz~{xuZbzqYEMfX zMEFb`z%*+_4Vag!K@e+6#zYnHu!ZW|m_AozvUs`lrf76ux|_z^Ut5cyHYSo~ct3TD zIx$Ak-qd$dMib+qst7iTzk&I zrHrV=_yd{A07QfdwNmqeE21^vw^uU^S1uDZIDGl3im#GkQ%S!%RtI=0UCs0%eKW7&P()wasO=J^ zx>^!gz#7{#VX?K&mQu!eP>Z9sTzrGzOxW5}6TEvv?hh~J*shx^gZU;Vz9>?3sAtu7 z8L5>C<*QctA@}N*aY$l}35tDj!^2o@kp`50#6ZNw!WgwJ5%yyUTO++dZr(^LY=q{f zE7;Qrh72a*TQMCnNKvwi7VsF&we!?)9)84QOlAtUIhrQy5V=OlsVgBbwO>w>?yuz? zjt$4$uasqwBg(6)df~-VV=3e-N%B0`MhYaH%)@+J6hs-VQF>kS7j3SI>+qT5N}Q-q z%(5a~CI+}S0~#;Cr%~5{q!v{v1Ie|Of`Ld+xR+0LFphu*Sx5>)+Rwx@lads2jHhF> za3Y!05G1WYH(|El2oNz@?jq9i7d~uMhfQZREgoO}FDqkdOR{qYRy4Z^F~>j^-C`9s zAHX%|Xr}Ufecla{f!C|J`l_Z1>(-+uD9WG`B{Z3oZ>OR;;O(xWplvv=w~G6)vFDGn z?$vUG=`05Bvs!W7T)W6bQf`}H6|cZ?o~P0t+AHb4!a&!+35r`!*(68~j+NkBrWQA0 zt>ayoqL&blbI{yBfL?Rj+16wp62j}NN5&^}oDPibfbKm$bBZks7#Xl5>?F{ z^jsONEh%z6t!AVlu2u1P*m{U3(!Z-|ny45;NuN+&b-()9J1cbmh_kMG5^LjlA@TEdY}z)dgW0NZ_gtKCRwo5l&H z$KT&83#kRk^~_b=dJbPE4!QBXt178DC#|>0=T)p~^l32bh)HNcuj)B#Ul+QOW8`nLE8<=zGt==DWxv3nmH78g z-gH@iX}a=H6h8)Q?HF*3BiZ0TzZX?ZSyh`H2=^K?GTeH@cu$pRtk0$YmPtKX9hW@l zy);o!PF7M=uT;MSA}kNxF0(=ff}@@+Z&Zd#v>C4eF{+Hk7~UqLCc?bUs8QjnxAr<^ ze>SnnBx4mNPe1o7a_4L~XGOHl6xr2lG(=cS9YY)vJ0&Tl(sljUX8wqS`C88v znQq0=DBoDswSj+;+_~vM?wx-AIz72Ro=!6aIOA& z-I;x#o>m;~spNzE0g;7WVz=OK?Y8pk)S2 zyzz6P6JhQVR?@TBZ=-3TmJhLhZZw``F-aLM>zbuMAj{L0BlmqZcWk3Wa{vq%@eN2{ zWV`FJ#ns=H53%pR@^8aZbIsp^9gRYPr><;k`2^?!Y(0F#^&x*q%}z5)e=Z~BtUq=V z6HA!#ZB(klO{O_KH ziaC-VZTALEnupL=(YdtM4iY2hFxxwfA`Vrc-+U`SMJ!jW#>OP_e;$iTY8_|b8efuDuI&ZGmGctdXZ*q zvu7ALV}H&>6JIY-SSD1qp9|bf zd+5jbZTbSMj3#w{Wmobgx{F9rqd^UvJoh+9X7`haZND!tIN`_#h-9B%Pd`)>F^zzj zJaFGv!OK`dRIW+#^$yBO71;}h@48QV7hXEv_d}F#h&~lVIV~v1^D_zs@@kX&@di0m ziL%J&!bSz%4b~4U`F0OJzMo8DYE18A{!I1UC3vh$w(vlZ2$O#^;eN4>7M}*ScH7Sbz$J*aPP!S~1PH!Jwe1^@ zJ~YOj7R|gDF!FfK8`z~28)V;-V|u$ep6Y*z>TQ1Bn{;4mGzp(P41Xu#Kl>WsvY7NQ z%A(hW{mUg{AJQw{l8w>aMb}E-mCOzZ1It565yDR%j|E#=32Go6-?=8V5|32wuSU`* z6y+X$`V#eRJ|d#w;F?kZ4lBFZ-i;~wgt-YKy-YWFa9qmu83FI-Kp}dH-gv8kweOIB zvQkWtB6isIb4W(!=!nHnF@f%_=G>UYI3yTg%s|kzd2ILp00YzfE|06m-N^=uW$bQZ zAM=b8{)KpiIW5Uo&{3wJkEZ<^wpPL%9E#XJ5a8rgM=?5*uNm_MRA2W`c{!JdP{LqS zo?4oj3~!GjFlZ*4OU{#;T1#}s8nxvF_O4=h$B-h7izmK3afcOSKNaUPbYHp?xjqvB9K$(E?vHl$%Mr!Op}~p4A<&NC&}Az%#O2*F*is#l zfx{v51XMiYHJ_*eaMo*Cy-XnV2(CoEy z@#X60JLU2@oSam!@Eh*w@~Jtr7PoAXSKR*q?&q#*Gm&j`cFz@A$;698j|*tpH0R3( ztY3Jl8J2X=iOZ0qD|24@#h&P2{zuFq4%u(>VEQBtW_ zMi~qZA>A!yoU|bS08KbL!t-m)ocvX90!GAiBnm(DK48=bF(uu>krJYWz`XftYQ|nP zI5-Nb~CMRe2YLmS9m{x=&AL&bI%!OB}0sps z(~%=Y?-_4D*oy~+7dAOKspAjBlanrcAK%zcU~>JB*N*lW9z0KvAf{c)Ts^;5$XphR^y!jBH(Hw6Cs z0O=#ti?TZN3_s&d+ zNd(S-@I2ep)%>CkDS$-qy;@4Rx-f?^0zL&5evECdghDqAy@(ZQF@-rJOF11$-nbli zFJQpjcUA(duVslGTdTC5OI*@GQ%;AjD)I?-E`Wq1{B><*?`e0y5p(;MINm!nNSs7F zY*^;3Qb;9uJ_$UWQc;ApZ!{{SVV_MF7bAQHR1SQH}V6HV2; zAWli<$B-2=2@=sP)uT^BYuS>BChj)aRF|+h@%C-WM}A9`Jk-#376U=a7Oy0LOXViZ zP!RIjd#FhD^lr877)8U}+~A9N>vc)(@fx@Xx@kt&-le+GNaxW(tIf=MVh2ZF?We{n z5CnG)BWJp9_}Y>IJXf%QJqoZDX7QAtfUf8W_(fUVyaHnuf^{7m!uN9}{HPozDnNM7 zG)I8?X;B?uE+coj^KZ3QfgUU902_lx+I*(qiD+NT#hzk=fckC0_?&akak5{yokwbb|VqCSE`XvOkssF8%s7jo$5G5 z@Ls`zy7vqPL2&8E5G%aZXKHR>yF}NDo~vG#%Og+fYuKL|hmf2fhNl~)nj}oIdJ;}+ z*`i3D;pTINJdq z^amX4k0o8+h-TytR*R5j#8u=T^!HN@gfUQXE@FFO6QDiR zt$RfuJW(8E^mzJeMvv2+%65m5>FpO;}@Mze$1q4FYy^8UK+ z@Wqs_hp|KCM}@qm7^CcpD2}m99U%`5)J~~ER9qqrh;vB%JqjJk^c4J)j+)SLhe4B* zP`HSPOgV2sU89{aUZ0voIk~SlhNKx;5ab$8_UTW_1;T4eGD$c9S9gXkRQAbU>YvAl z(eB(4_$>>90g%#=j$`D~IjMb1AtS1gYIF^cBiw()WVMv=Nyij-T9Q>aS2utl3MyCx z6WZVz6CE8vsGoa_i`z)m$Q_VUSVm^I9G%n6YQk}IE8cQ>7<2$oo-s5qV~RO)U@O^( z<|Mi0B(BwQIi3mNfhCbM)d(~bNMJA^ozHe8bg8|iEV1w1PlPt}2_@`=)x43*)}^?OBBH1V7d}Rjte>arXcMuci>mKWK^Y z_N|e#pHJ{VXQZ_eq9<21Z8f=>JD>|s9uX3(?H1nZ#!xa>9kbk4;LUe`Wf%yaf|AIX z6WPzC@Er4VPl%2E{$|pr3GOq&2%JK-d2PT*t$=fEHs!rsye1dy_*#iJNPhXNh{Kek z2)VWh9#DG_4O+Q3Q%-^n)9wTnaW-u(pnyySU6Y@|Ue$&)P6i!qeIBR3FA+T*YFp=# zF5(%7M(#~q!qJDTXoMMn2Zl;_kilsLyis89;qRbJ6~8%9_8?cYqGW=Ia+a2H0if)hx|)vKG;}?PUkR>R()K7sqD1UAKLvYd568CW z3_Olhnt{l+xwOGYQ(n=AFbZ>CW1v2jY)FFU(*-12_LOKcoYR_~K>Ac1qP(=WNkq$6 zF0xu$!hmpsa{<^^G&9SAZil<=LTM}6w`)0ryNb#gP1pM%q%vV7^ih#lBqaAYe4~kK z($rbcZPCnQy4235=H~PS!V0qzE6Ld@FX55Gc2WEd$Ji!U*Pm<}N{DVmGgZ;kNqOjN z5&nTf80Is7RY3ulN{oV^hz5g%N<24c&cJcsZ)UiC#b>C6@ z9Au9Y)1U9ey@%#29RC1nG+iA!kG3eNp(>^&r$EQ?6!!}e643zo@m?@9o=SWz=h!oKF`APmgcdp4njF z>C+wa;q50!DW){n_xDf4nftl@n{4o1zrmik{lt+7r3MiyNChbm?vX&Sqmk#_(D9XZ zbjcYN8ihnrfS_~(_kPiwO{SRB_8xbeZ8i9+R4JbO~o8b1QihO1+`Bm8dEbi+%XQ2F%*!5 zn${<$1qICZtY)qejyT_wzGz;l+`#z8^j$EKf=|ar0nS*8cuseCQ{#=QK0}XaOHt*# zslr4R6QOtWV)TeO=o4^P)oJj%n6!>1s2)FoD8`sGL{q@a$3q70&7=8LlNz3Cd`qWAh6m|pnUsL<20Wmol8r1Im zSi$7Ap1u9(UN{C?76HULI#rC^1WQAs%;TvhrZ~8U_E26QP;1-=`$tx@7@)R> zaSO}l-M{{S#} zJ@GE$V47F{tW*~-E2UiI!E0kL2_s6ph{zG#8J(Ll+k6 z>u`?evMDtSjxCkV;G9mi?YII6oyka~BBzs&a`2oH5b!WAQdxB6gs=T&O5oi>;{g-4 zltS($mrDNt)>k=h;dIx}U25WYGe3ppqYIDZFB?h&_f_h1QG9EHr}8xyC(#+ge8?2T zVJbFP6wjdRsXmR;t=Ocp;NeI(H0UH(>{N{AuumBBPzPSLPNB*;Px*+&efOs3vAQxB z`D_IXGr7><%e6|O(Y(RtJS|`n;@IgW!8ASxH$~K+P-!wH!5AQPZdKE8j#K_@Vt@yX zIj#>O(u-&lo(p+x(xH&WCn5^C>IgMvq32%Fk5kgKuKGTo^J5gNxsqcLnO+$%~W)N;Za`0AH+zq!~#N zPk}j>Z@_b?24qfZ>OM!E3op2N&Aka^_tT-Nuj2w(iAp?% z{m-E4Yp)5xM*IT-;r9S8-;kw=xuNE#6yeF=X&>V>@se*K>VQ+}6jxG%nTvFw+IQ3b zZk6}g)BFhN$Y15K9urJb@@NT9=mn`TE#%_8?;{2jWq*Q&&&NEpJ3}hJ`%+JmW|SpLW5I@DB>|IDN+N>Z7t*|i z%hXS#@K4O8I1CS%HRSW4G8?I6X=I=rx<4Rlv57Q*qnn2`Hkc2>mc@Nw?1S%lOH7%xuIKf7*Wm}6I zQ1$K@<7jlRX%Q~)!X$Vek-@sXyt7oo`5>$K!cHLO@i3^RLm{3lNiU)$CE^wlA!_z) zjAn)}j|Q+FXSjz<071g}wGIYiL?yW;HLw8?WYG3$LlFmT=|SYmNC-b2XrHWBgZ-|phZfuPcs=nd zB|^1f&+Dgjsc#GEDiotTO$_#snHB1553#@DfXAR}zMRh-)}wcck76nZ-sXF#!WsPl zG;BZ^{ z@GqhhpsC^D-9U&P)5(CRBYUgcB}m~~u>#^3A9*T$ZJ-`X0NcWNRx~!y6WMF%RM3bZ zw?7?RfQW6Nuc23Qh+(#?mok_l{{X##sW}*mROzHv>C)Xlkc61m_G`cpeyVu8yqgo> zYzj9V;j%1J-z09d*2;ToMmo@%Pu3`VJxM;CI+puM_vE_n^kE?Vxp0g5Rip1{X32`@ z6fRUncV{`w5*q%={{Zvr)Z@a*0pFZtub~y%M7uQ{`~yFCiz1951m0arepHfxl0lM~ z;)=5|PZ5X?MMZm3*xrvxBlzpvSE6bCAuY;`I_bQ+Y57t1kV$8YIuxiNCLY>2_%UAW zwK#hztJdteRAHve* z?O?D+!i;~7>U0tNCUowOafbC31S*W=YUaXd>EU#j(UOtg@>>4UkM-0er1R(t&>f;YCp3|XQn)eE zfN&uxkPvjMi3BpIXBF;A`reU0-RtN*E11bBSDg=NC+nzggz4)WR_SX6Lkuye;TWg1 zh|ep&m`y9Num)60xs!z=c5z%;DhYo%D#}1oXzE$f{{T46abuz|MQ@mlKN?s60BZ{x z==ngBRlpL>Gr3H~U4?)$#+7vDDN?u(R=~k(nr~LOJSQp9u-GDud}-=Va3x26i2ndE z^>rFeM#xL}oGl9zFXFta)gEg1maE9}^7jZS$r1A0y+nG$&MDTO%9CWZi%seqp#+-r zakdElH6kE8pd?YXG-eL@kS8Hfc?jOE$P#JMzlXR07fz!D8UPR6`$3~;L(w!`e*=9# z9f2PS#xOs3G+Ik5hBJENpiO;P_dmJ5=R9TsEg}HYumHL9KgevA)-Vni4MwFocr5OU zMMMzso_pu6)&BsIH$=fw+{})Z+QsgPg1Lf|dYsVdXC>J%)xHc33DKb=?vYR(;!EqD z(Hfym1;w1YKDB5=Bk%Uf102tUT$1JoDIl)!&v;5!g*X`dBpUBzmxqH`P!b+k0E0-O zN61tplxUw*&bVSO5$UKH#ed|z@bGI;q9MpGI%2db99LI6q*)lsinl`7ovH?LjW_vlfY*wtXz`2H8N}r8eOeYe{He_ZVW}rES zcwb2)`5KG<>F?(<@Vq7e0KJyRa}koRxlcT=ys~`Spw7FTo@AQ#o3xYV`?r-rdjw;8 z&V9sP9tB(Az+pNxBy`k@fbSG{*E^tZ3Hi)yXEEJD)G^yX0=K?#L^=BA&M&tox(o~}Rdu4jGaf5x|G{`BPYspjKpzrcK!p}~qpIC#kJ z4M1}Uzqm509|`%)Y-i9*R}ndy;&!!%jjCMnM4xEOo~5h3l3o!F zMS~EC>S80fH1U|hyq|s+d#zjCEyQm$*>G2&`FLF>kCT-C9y|bv)!6g`k1-X^!xx~O zQ~~!%lg&@;+eBmtYUUM?pzWX7xw=RQe!%|X-aW!KTtJeQ9g8S5xY8En8lEs^h;s!U$ear@jVi;yubO3~38f#tgBJh~3AB1Wz;fMB4_O22wkNLTI3xbJ^d=-2LZ^ z-#PG${{Rnj-d_)9;7=vorFSQ?flrXp^}_>$;UtR;5dHURhG&A}54h|59?rNBp&eiL zWPDr}IN`7C+TFFd)3_h70mrGJp|dU`#5QB5I`@lH*9`hL3S1hr!s zf5h??i2ndZO$0)J{jrMQygQd2f|`QhO)MP`K6N|FKzjG8%6@@dNWVd@EOhEuDsV<> z2{(^U6qXEt)*!${hT`|Br>sLe)pKuS{K)bo8=MGbOTvUAIf#T8W z`8po4w{U3m1b}=8Q=USiKx2QzRME#Qvr9rX5;*A1j%J5oMtmOVM_>98Q_3QeM2km; zyz9{Cz;yug6gtK}rC0oPNSySx!i>bYMCE#1+E4hR)AS$D1B{cGS8 z1~-5?EmOi~6MLwj4$!0lDE;G)0oR{Oc*urR%~h*onNy)1yAKi60u4o`kMnRUYiQ(i z>Qoa#OuNa@ujFVb4o7p2f9qmH$1jtLPVcy9snDBSyMAERt?AMjCMjOYxG_b-mq&kB zYW6IoK=W1N3BS0U#E}Eg0bb0D331*?+NILkOXTG9NINO)wRif(_9}cVE)BFc_K(BehgM@x86_^Btq5{vpD8X+b*bVHI$M%1CvrI=U=xbHn87?=(GQ{ZLiK%d zF*vzDW9j|c7pOR|IWkp*D)alX(dKhgTr3w>w{n?!_ga?2GKg#1au{ZU>SHZLjOXMA zYUSI3ZD~1)AwX!`3GHsT#OR&I^=Iu80OOoV>hb!W#3a{HLth4x%q9UfNoYW9>)V;3*G!s|j}me!VnP>6%M zD7?*Zgx1sDBkdXPgs>G2+{tc`&Q^{1ihN1U#cBP`m5F{l@_L0C)~@Z{_+HWtly&Zf ztJ$)M5*t_0%Kgh4kq+?QNM1-^u|q=kNoTt4Zj~|#edc)!yqTw*+Rq+Nhi;W+dkut< zZge}T*pXh%fgllN=wvkwYDLxqEP^u{#aN-5XG&%`T98+~@&;vx|w+^~*EFp~|>an20 zf#d}t%DOm;zN8VV3XP!!a3 z7@^U`bUHhyI)gkYo*-2zj>Ta>pqhV*Ng+~AM}m>b7-d?1ZXt{&d_Lr;7@bwk8xZ(diI)wJn7d(6rAF_6TaJ0E+v*ek3#h;{{U7hDMYhxHt#I~%EB%9 zmoM^K7bub$VUjpzPsX16K_x{${jtpC?w!faXpbZaxpHb~6uGWEAFOzBo8`ZNjG4Hc*G~DbYf4a02Q4E9m|E?2 zCvEnt7^k**JH8U9CJ5ilGCdx0T|Do;)$G{Gf_M0|BC_$qhGGN)y_s@t?VvJ{qOAxp zBha~i4XAR7p6+Yf!GX1NksFHlB>wydv`Hiu(fMI&ds8s9y$q!v&>s(c{B+A= zl#D?c0JRywwvuU3lxp^LB1vM1>SuFSSd)~bq@u2FQcuxWd79+M)TpTKpL|M6%$?PN zt_5!o>t7#nKkW5y3rnw_1zZw&!#R#2vdN+XgGtf>e+3W~rco&*I5X!?l)i$){ys(NSH- z5fkL~sQ&<4D~Vy7?l_^#j+X^Dt5hj%X={?Hp5)d2(3Tp%x)Q@)*O-*h^VHM>NG@Ju z8425EuWSNkev$epsBO?A_DNQ~oZ?93_|ceu^H0iK$Y(CVchFQ2d$iHrpZr2qt zBA4@KZwvS0&1XC4QcpEBk(i*dwsKHssd}DMG;q%;iQp!TTnHe8B_K*SFDTIGk)v|* zjSpj2FCj{bAdy^4DMpXchkRB3Wtv`rc-GpNoy>cekj#pICyM0 z*&6pIO7?S?lG)tob^{Nv6J@N zlT!ku9^lsH;NwhUk`QX>2*N?|K^W{R_VmvdhqX%!qUX$lD8SJ^l{r-3t|CE&uWDRK z@VsXvD^yuQekcC`uMZr}Op}{yt}oki{R0YuEnix^xp`v4^1&-mz>&T@zyrZ7j2^pH z)`QwH_me}BR(Gj8>~Vw?mge2Jpas#Jg4`!=)j8>MA9fD`6CaxtjBr{54co@{6Bvw#4 zFv7q6)~RnKm}q+%KGRCNfI{(0Hwog$k)%h2*>P}@MtWiD{{RhhWjmC^j_B2Hg&?uD zkde>3n!US{Od-xusu_v1ls;QS*oqF4!e5ZtOT(ds@>9r$vfr{yiovk4py`qkbR(peA3P(~sXyyzhQ zy1Ac^{iC?6o8v5At5To{pNOcE5b)Qs@M98me>62HDA!V4f|1l??abto>}f8RLs;-j zegS|u4;QGbDFo2j$sjV$)op#nxP;22#DI?rm{dhC{FN1%iRSg^R#Ou@@)``SOBV+2 z&EHMH?xwN~*OE>h@#KI|k4dacsy=@BYPOtA_mfDbPmge#?S;QeF48+a!jV|wj%1mQQM&SXd4X=~Xm8q$v=JKU*> zD(V?bhqKDdQDw({A96(?&|F1wqB$$tfs2UxCnJQRE0UFSeA310wResLl{BQAPB2L! zK49|%8nQwyEce~2dr*x8SjlR917%Ab>Ft|2i=Z3LhTVc2B&X{7MW@LY=f;_PWj zbrzP8a*o~*a@8zcE4w#+Hx)KG^UpAIk01qmHsN>ZG(n|U6Nq1=$Z6psSzd7E`Fsc} z7_jME!}{08D?=O2>xQY2#^cb88i@mB~`0?gbp`P zwu%T3SRd=r%#v^g9$e4n~&aKZNb3h%fj?AivyQ03to3GZ2@$H`prW69w5hzYFPY*A~T$)JS|*C zButDP5t06iW>+Cj4piiS8zEA`P7foI`<=n#h{N*kFlz1hxVo3C{{S*Q$NDNEmSbP} zxh=lMUz|fMT-Kcou`T3UaTcK^4aKx0LPwQz4Y<_CM^M!tTdQa?lx?1;w?PfG19@es z$XKnAZ!5pu09528b6rFPWshP-T_d!7B-W2q*sqT{@Gh!mLnwSVT)0jV9>0%~R1 z8noNXARLiaBmUiS@DHWyQMRrTfzptDVi4>mN5 zxwgAaSg)9?V~Ne84I@59jar>2OOuO`-_cMVC6>}4V-4k#P)H)0%_08&HuYM%_LQVg zrelB}%xc)_EVhw?fxNO!BN*k1Rn_8Q*@X$^cZ$t3!Wr*SWoEaXHIgIhDk``b|Y{1X0`ucbbwzZf%pi)Pr+m5&r-n z^*U!x>Ut4bnyqo0+tegX$Ffur36kc|;E`&^RmAgy$w;VgjfRQJH=nr+LwHHR_@3=hzEf`DNU&Mq*zh!jIAb?I@7t_;E4`h2$ zE|b+q#wvJ6yL!9yq3n|L`pC@0)u9XTchM8aWA{0MinI#i;?taRp|5{0CC%6S1r=(7 zWSZOHkqud17-P^?-OmExxaKp`kixf;_d_8dR91%K<>?&93)~@*=fzm+OF{5ThocHk zNgsD(&{xOq7=6B5SG5U9tnPA}5!AIq&Uj!Bju*L7YueKQ#ro)RLPShS>TCPju>;<2 z?m=O(>;*(6#PEC*0w}5Bw4jv@+aOWA0{9qM@9356$tFl6hCNdVGAZKa9Kng0)KZ&c zZ|5x-Sj%MyDRX$ZmtQc4n3Fk)z_xu_=u{uDd0 zz@YhgAESy2Ha=&$q192rG_H7s{{Y4*N7^Q!gq3*6Z4FHVyS;tGAlvfr_bGotuXBs^ z3QX_)$bIYDQt7z1QKch-@cXD6in6=X9qkJ!a?N>AbSb z@LQ#MCAiYA`C8w@$z1t0dyf>u+iG5+91{}j*I@Q*Ruh;e)1y)8yM?3Bj$qRD0P_^x0XQa)FuCSwP8&uC6=`~7k9-XhywQUS z9a)YA4(P$C^9!D4hg4StkHf<8_;$9{{zarbl8c0UgiR%ej|jywc{5a&@w>N~DhV_? zs<WU#SXeSY=0!X(=hII?uLb{%;Fa!po&O%-S_QM&zR;ioq~r8PXI5R zyVHz`5af7pa}@ABWh?=Ev|9IqA$d$4)%{|^y z-N(Qx$|n(!PeKkxD=&*n2%|-TLGH~`&oMxGNZ^Ws1xZ103`_?+)+7d9FfJRrinR$O z!TN9yEd@;zh_4CeOK#)Oa5tc^ntq7*2vv?(RB%Tj(ts)Z~H?ScHJejDLQju~( zLFy?jMDRfTlHyvq`xk!95Apdp740bv+|HhDRtzrS`K5a*%uVv1N}6FR2Z0>VA8Zz@ zWO4*H$Pp0sa6IvTsi$rrrNbV;PDneL!5kuNi z(oW!7*}{O2H<6m2>=b!%yrZ*FxRK-sk$Ou+hFMeLd1)^T%j$uvh1A)@fGSJP?C%~O z8aJ)ba3{7^cli-a0m14&o+YH9zhTL#tz-ZWWSvb5Wds_B+D~&FGVlt`0 zxkiN4W?kO7e`jU~mH?iCYW~zgYLQ!dkBkn4)RV=bqj4V`kB&6YRQ06hZKt*&d42K} z6IMitZ4;@ErR{TS=@eeBX)VBp^4ceT8@e2G0)zxyi;cuZ`)D4o#6}5Wgt&YU3GVrB zoSav&XM4D%?I=9s6g`?jm$r6l5)W|94>Te#K%k2DoCubQ6d$ZF!fE}YN>Q*tBkrqNE%d~D1esb(5&)~<*FGHct@Kp zf8_nuzB;fjZmn(l==FGp97$|ZVh52dO?wtpGo&;nN18`~ph}C2yVtb}%^l1U7t#BM zsEolSnlcIH%#ql5Nq7oWv)t$k9KKS7Jk{zSYCX(RIL7#?P^SV|3M5VD&q6sv+JII^ z1))pn7EwVy8mTqRfqK+qns!@y!Ph{J~?qc;IRp~KL1Ns!gevB`^*YIdtPO(coUOA0%D$fIiFAF1~I0RH!jc!X4< z5+Lk@dJ)2Iyysm9*cAW|1^9r(2dO2qQbQcanHha+*_`iZ4#$lBqpKJMhSttEJXiGz zP!M4;SxlkoSe4xf!BR&t{Epgkl?9+g{8S1a(ohGW&VN9pf*PLHMvVgnZ?^ zl<**@194)760sdTtH1u`4vM1yQ@cw9Zb(-qr*J2=j+HbMYxctVW~FlOBV269xv5-3 zzg+>NZ^kq3G!Is?@Dcpf0sQTT8CYZyzn!cO2K`Xp9%ePv&Wxa*5M4 zMhG<{Y{EX~li@BokH(+k&~raCQ{1zq&Ac2~%60=4Zj2@q@@t<>E4%b2lcGj2C_eme zT62~?nEH-}lweQarYa&z4=mT*L0Kn;y)t_EFu{q`701exn1A#=tm|_TDY_P3%qyoB z7$kQ5nBsvh@;_H&)c*jZn-3hHam7S57cpG~t;5O$qFG+OmNXX9^4%z5_(pK=IjAOx z$TtI!LJe4FK`+5F9;#|#q_>9XJ_mh3t1E;uJg-(fxKi|)D8>GGk>@>-RlG=1`spgr zfkHkF zwX8hED_+q9ivg5I=tWw~&ebIhVP7(8+TGGu^&$K&uH5^R$_DA#)vMXZcx@70+1{`D z+}TR{9sJbPGKpn~OeK<-Wu40@eA`=aTv-6Tp z+qDFY7`Gjsy^*86tf!Qu0ZyFEvx#0)*#f@o-=<}H;0UhlrG8Qm zMxsHQ=v@gU$C6@^LhR=xz^sQ^Es~8#$lyIp-~s09v8%!1CUf?=Ac4=LgK!vRlI8w z7wSMBE<+O$0P?GN#Asicz^|&67$6B;5k@uueiWPKAJxq>2PPr`+BCz?}e|Lr@+C#EB@^ ziAG>W&~dDz$Qn!B|!s1@I?>;O5)ts z@KwE;o-0{p2#)n@a)mDa^*`k3D#F%3c7|SS @efOz8MAI2N<5-E9vtNT~?m}2Cd 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 payI3fEGFrBm~pI diff --git a/samples/js-menu/data/menu.json b/samples/js-menu/data/menu.json deleted file mode 100644 index d46739205..000000000 --- a/samples/js-menu/data/menu.json +++ /dev/null @@ -1,97 +0,0 @@ -[ - { - "title": "Mozzarella Sticks", - "price": 8, - "description": "Crispy fried mozzarella sticks served with marinara sauce." - }, - { - "title": "Chicken Wings", - "price": 10, - "description": "Crispy fried chicken wings tossed in your choice of sauce." - }, - { - "title": "Nachos", - "price": 12, - "description": "Crispy tortilla chips topped with melted cheese, chili, sour cream, and salsa." - }, - { - "title": "Onion Rings", - "price": 7, - "description": "Crispy fried onion rings served with ranch dressing." - }, - { - "title": "French Fries", - "price": 5, - "description": "Crispy fried french fries." - }, - { - "title": "Mashed Potatoes", - "price": 6, - "description": "Creamy mashed potatoes." - }, - { - "title": "Coleslaw", - "price": 4, - "description": "Homemade coleslaw." - }, - { - "title": "Classic Cheeseburger", - "price": 12, - "description": "A juicy beef patty topped with melted American cheese, lettuce, tomato, and onion on a toasted bun." - }, - { - "title": "Bacon Cheeseburger", - "price": 14, - "description": "A classic cheeseburger with the addition of crispy bacon." - }, - { - "title": "Mushroom Swiss Burger", - "price": 15, - "description": "A beef patty topped with sautéed mushrooms, melted Swiss cheese, and a creamy horseradish sauce." - }, - { - "title": "Chicken Sandwich", - "price": 13, - "description": "A crispy chicken breast on a toasted bun with lettuce, tomato, and your choice of sauce." - }, - { - "title": "Pulled Pork Sandwich", - "price": 14, - "description": "Slow-cooked pulled pork on a toasted bun with coleslaw and barbecue sauce." - }, - { - "title": "Reuben Sandwich", - "price": 15, - "description": "Thinly sliced corned beef, Swiss cheese, sauerkraut, and Thousand Island dressing on rye bread." - }, - { - "title": "House Salad", - "price": 8, - "description": "Mixed greens with your choice of dressing." - }, - { - "title": "Caesar Salad", - "price": 9, - "description": "Romaine lettuce with croutons, Parmesan cheese, and Caesar dressing." - }, - { - "title": "Greek Salad", - "price": 10, - "description": "Mixed greens with feta cheese, olives, tomatoes, cucumbers, and red onions." - }, - { - "title": "Chocolate Lava Cake", - "price": 8, - "description": "A warm, gooey chocolate cake with a molten chocolate center." - }, - { - "title": "Apple Pie", - "price": 7, - "description": "A classic apple pie with a flaky crust and warm apple filling." - }, - { - "title": "Cheesecake", - "price": 8, - "description": "A creamy cheesecake with a graham cracker crust." - } -] diff --git a/samples/js-menu/package.json b/samples/js-menu/package.json deleted file mode 100644 index 1488af432..000000000 --- a/samples/js-menu/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "menu", - "version": "1.0.0", - "description": "Genkit samples for a menu understanding app", - "main": "lib/index.js", - "scripts": { - "start": "node lib/index.js", - "genkit:dev": "genkit start -- tsx --watch src/index.ts", - "compile": "tsc", - "build": "npm run build:clean && npm run compile", - "build:clean": "rimraf ./lib", - "build:watch": "tsc --watch", - "build-and-run": "npm run build && node lib/index.js" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "genkit": "^0.9.0-rc || ^0.9", - "@genkit-ai/dev-local-vectorstore": "^0.9.0-rc || ^0.9", - "@genkit-ai/firebase": "^0.9.0-rc || ^0.9", - "@genkit-ai/evaluator": "^0.9.0-rc || ^0.9", - "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9" - }, - "devDependencies": { - "genkit-cli": "^0.9.0-rc || ^0.9", - "rimraf": "^6.0.1", - "typescript": "^5.3.3" - } -} diff --git a/samples/js-menu/src/01/example.json b/samples/js-menu/src/01/example.json deleted file mode 100644 index 10c76a2ea..000000000 --- a/samples/js-menu/src/01/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "input": { - "question": "Which of your burgers would you recommend for someone like me who loves bacon?" - } -} diff --git a/samples/js-menu/src/01/prompts.ts b/samples/js-menu/src/01/prompts.ts deleted file mode 100644 index c5157cf1e..000000000 --- a/samples/js-menu/src/01/prompts.ts +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright 2024 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 { GenerateRequest } from '@genkit-ai/ai/model'; -import { gemini15Flash } from '@genkit-ai/vertexai'; -import { ai } from '../genkit.js'; -import { MenuQuestionInput, MenuQuestionInputSchema } from '../types'; - -// Define a prompt to handle a customer question about the menu. -// This prompt uses definePrompt directly. - -export const s01_vanillaPrompt = ai.definePrompt( - { - name: 's01_vanillaPrompt', - input: { schema: MenuQuestionInputSchema }, - }, - async (input: MenuQuestionInput): Promise => { - const promptText = ` - You are acting as a helpful AI assistant named "Walt" that can answer - questions about the food available on the menu at Walt's Burgers. - Customer says: ${input.question} - `; - - return { - messages: [{ role: 'user', content: [{ text: promptText }] }], - config: { temperature: 0.3 }, - }; - } -); - -// Define another prompt which uses the Dotprompt library -// that also gives us a type-safe handlebars template system, -// and well-defined output schemas. - -export const s01_staticMenuDotPrompt = ai.definePrompt( - { - name: 's01_staticMenuDotPrompt', - model: gemini15Flash, - input: { schema: MenuQuestionInputSchema }, - output: { format: 'text' }, - }, - ` -You are acting as a helpful AI assistant named "Walt" that can answer -questions about the food available on the menu at Walt's Burgers. -Here is today's menu: - -- The Regular Burger $12 - The classic charbroiled to perfection with your choice of cheese - -- The Fancy Burger $13 - Classic burger topped with bacon & Blue Cheese - -- The Bacon Burger $13 - Bacon cheeseburger with your choice of cheese. - -- Everything Burger $14 - Heinz 57 sauce, American cheese, bacon, fried egg & crispy onion bits - -- Chicken Breast Sandwich $12 - Tender juicy chicken breast on a brioche roll. - Grilled, blackened, or fried - -Our fresh 1/2 lb. beef patties are made using choice cut -brisket, short rib & sirloin. Served on a toasted -brioche roll with chips. Served with lettuce, tomato & pickles. -Onions upon request. Substitute veggie patty $2 - -Answer this customer's question, in a concise and helpful manner, -as long as it is about food. - -Question: -{{question}} ? -` -); diff --git a/samples/js-menu/src/02/example.json b/samples/js-menu/src/02/example.json deleted file mode 100644 index 57a597769..000000000 --- a/samples/js-menu/src/02/example.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "question": "I'd like to try something spicy. What do you recommend?" -} diff --git a/samples/js-menu/src/02/flows.ts b/samples/js-menu/src/02/flows.ts deleted file mode 100644 index 4b3f66b8d..000000000 --- a/samples/js-menu/src/02/flows.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright 2024 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 { ai } from '../genkit.js'; -import { AnswerOutputSchema, MenuQuestionInputSchema } from '../types'; -import { s02_dataMenuPrompt } from './prompts'; - -// Define a flow which generates a response from the prompt. - -export const s02_menuQuestionFlow = ai.defineFlow( - { - name: 's02_menuQuestion', - inputSchema: MenuQuestionInputSchema, - outputSchema: AnswerOutputSchema, - }, - async (input) => { - const { text } = await s02_dataMenuPrompt({ question: input.question }); - return { answer: text }; - } -); diff --git a/samples/js-menu/src/02/prompts.ts b/samples/js-menu/src/02/prompts.ts deleted file mode 100644 index 9ca0519ed..000000000 --- a/samples/js-menu/src/02/prompts.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2024 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 { gemini15Flash } from '@genkit-ai/vertexai'; -import { ai } from '../genkit.js'; -import { MenuQuestionInputSchema } from '../types'; -import { menuTool } from './tools'; - -// The prompt uses a tool which will load the menu data, -// if the user asks a reasonable question about the menu. - -export const s02_dataMenuPrompt = ai.definePrompt( - { - name: 's02_dataMenu', - model: gemini15Flash, - input: { schema: MenuQuestionInputSchema }, - output: { format: 'text' }, - tools: [menuTool], - }, - ` -You are acting as a helpful AI assistant named Walt that can answer -questions about the food available on the menu at Walt's Burgers. - -Answer this customer's question, in a concise and helpful manner, -as long as it is about food on the menu or something harmless like sports. -Use the tools available to answer menu questions. -DO NOT INVENT ITEMS NOT ON THE MENU. - -Question: -{{question}} ? -` -); diff --git a/samples/js-menu/src/02/tools.ts b/samples/js-menu/src/02/tools.ts deleted file mode 100644 index 0c280f949..000000000 --- a/samples/js-menu/src/02/tools.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2024 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 { z } from 'genkit'; -import { ai } from '../genkit.js'; -import { MenuItem, MenuItemSchema } from '../types'; - -const menuData: Array = require('../../data/menu.json'); - -export const menuTool = ai.defineTool( - { - name: 'todaysMenu', - description: "Use this tool to retrieve all the items on today's menu", - inputSchema: z.object({}), - outputSchema: z.object({ - menuData: z - .array(MenuItemSchema) - .describe('A list of all the items on the menu'), - }), - }, - async () => Promise.resolve({ menuData: menuData }) -); diff --git a/samples/js-menu/src/03/chats.ts b/samples/js-menu/src/03/chats.ts deleted file mode 100644 index 4a3ab51be..000000000 --- a/samples/js-menu/src/03/chats.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2024 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 { MessageData, MessageSchema } from '@genkit-ai/ai/model'; -import { z } from 'genkit'; - -// Our flow will take a sessionId along with each question to track the chat history. -// The host application should keep track of these ids somewhere. - -export const ChatSessionInputSchema = z.object({ - sessionId: z.string(), - question: z.string(), -}); - -// The flow will respond with an array of messages, -// which includes all history up until that point -// plus the last exchange with the model. - -export const ChatSessionOutputSchema = z.object({ - sessionId: z.string(), - history: z.array(MessageSchema), -}); - -export type ChatHistory = Array; - -// This is a very simple local storage for chat history. -// Each conversation is identified by a sessionId generated by the application. -// The constructor accepts a preamble of messages, which serve as a system prompt. - -export class ChatHistoryStore { - private preamble: ChatHistory; - private sessions: Map = new Map(); - - constructor(preamble: ChatHistory = []) { - this.preamble = preamble; - } - - write(sessionId: string, history: ChatHistory) { - this.sessions.set(sessionId, history); - } - - read(sessionId: string): ChatHistory { - return this.sessions.get(sessionId) || this.preamble; - } -} diff --git a/samples/js-menu/src/03/example.json b/samples/js-menu/src/03/example.json deleted file mode 100644 index 19ed21af5..000000000 --- a/samples/js-menu/src/03/example.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "sessionId": "session123", - "question": "Do you have anything healthy on this menu?" -} diff --git a/samples/js-menu/src/03/flows.ts b/samples/js-menu/src/03/flows.ts deleted file mode 100644 index f71bc3a98..000000000 --- a/samples/js-menu/src/03/flows.ts +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright 2024 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 { MessageData } from '@genkit-ai/ai/model'; -import { gemini15Flash } from '@genkit-ai/vertexai'; -import { run } from 'genkit'; -import { ai } from '../genkit.js'; -import { MenuItem } from '../types'; -import { - ChatHistoryStore, - ChatSessionInputSchema, - ChatSessionOutputSchema, -} from './chats'; - -// Load the menu data from a JSON file. -const menuData = require('../../data/menu.json') as Array; - -// Render the preamble prompt that seeds our chat history. -const preamble: Array = [ - { - role: 'user', - content: [ - { - text: "Hi. What's on the menu today?", - }, - ], - }, - { - role: 'user', - content: [ - { - text: - 'I am Walt, a helpful AI assistant here at the restaurant.\n' + - 'I can answer questions about the food on the menu or any other questions\n' + - "you have about food in general. I probably can't help you with anything else.\n" + - "Here is today's menu: \n" + - menuData - .map((r) => `- ${r.title} ${r.price}\n${r.description}`) - .join('\n') + - 'Do you have any questions about the menu?\n', - }, - ], - }, -]; - -// A simple local storage for chat session history. -// You should probably actually use Firestore for this. -const chatHistoryStore = new ChatHistoryStore(preamble); - -// Define a flow which generates a response to each question. - -export const s03_multiTurnChatFlow = ai.defineFlow( - { - name: 's03_multiTurnChat', - inputSchema: ChatSessionInputSchema, - outputSchema: ChatSessionOutputSchema, - }, - async (input) => { - // First fetch the chat history. We'll wrap this in a run block. - // If we were going to a database for the history, - // we might want to have that db result captured in the trace. - let history = await run('fetchHistory', async () => - chatHistoryStore.read(input.sessionId) - ); - - // Generate the response - const llmResponse = await ai.generate({ - model: gemini15Flash, - messages: history, - prompt: { - text: input.question, - }, - }); - - // Add the exchange to the history store and return it - history = llmResponse.messages; - chatHistoryStore.write(input.sessionId, history); - return { - sessionId: input.sessionId, - history: history, - }; - } -); diff --git a/samples/js-menu/src/03/prompts.ts b/samples/js-menu/src/03/prompts.ts deleted file mode 100644 index d332da7b8..000000000 --- a/samples/js-menu/src/03/prompts.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2024 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 { gemini15Flash } from '@genkit-ai/vertexai'; -import { ai } from '../genkit.js'; -import { DataMenuQuestionInputSchema } from '../types'; - -// This prompt will generate two messages when rendered. -// These two messages will be used to seed the exchange with the model. - -export const s03_chatPreamblePrompt = ai.definePrompt( - { - name: 's03_chatPreamble', - model: gemini15Flash, - input: { schema: DataMenuQuestionInputSchema }, - output: { format: 'text' }, - config: { temperature: 0.3 }, - }, - ` - {{ role "user" }} - Hi. What's on the menu today? - - {{ role "model" }} - I am Walt, a helpful AI assistant here at the restaurant. - I can answer questions about the food on the menu or any other questions - you have about food in general. I probably can't help you with anything else. - Here is today's menu: - {{#each menuData~}} - - {{this.title}} \${{this.price}} - {{this.description}} - {{~/each}} - Do you have any questions about the menu? -` -); diff --git a/samples/js-menu/src/04/example.indexMenuItems.json b/samples/js-menu/src/04/example.indexMenuItems.json deleted file mode 100644 index d528d8ae9..000000000 --- a/samples/js-menu/src/04/example.indexMenuItems.json +++ /dev/null @@ -1,57 +0,0 @@ -[ - { - "title": "White Meat Crispy Chicken Wings", - "description": "All-white meat chicken wings tossed in your choice of wing sauce. Choose from classic buffalo, honey bbq, garlic parmesan, or sweet & sour", - "price": 12.0 - }, - { - "title": "Cheese Fries", - "description": "Fresh fries covered with melted cheddar cheese and bacon", - "price": 8.0 - }, - { - "title": "Reuben", - "description": "Classic Reuben sandwich with corned beef, sauerkraut, Swiss cheese, and Thousand Island dressing on grilled rye bread.", - "price": 12.0 - }, - { - "title": "Grilled Chicken Club Wrap", - "description": "Grilled chicken, bacon, lettuce, tomato, pickles, and cheddar cheese wrapped in a spinach tortilla, served with your choice of dressing", - "price": 12.0 - }, - { - "title": "Buffalo Chicken Sandwich", - "description": "Fried chicken breast coated in your choice of wing sauce, topped with lettuce, tomato, onion, and pickles on a toasted brioche roll.", - "price": 12.0 - }, - { - "title": "Half Cuban Sandwich", - "description": "Slow roasted pork butt, ham, Swiss, and yellow mustard on a toasted baguette", - "price": 12.0 - }, - { - "title": "The Albie Burger", - "description": "Classic burger topped with bacon, provolone, banana peppers, and chipotle mayo", - "price": 13.0 - }, - { - "title": "57 Chevy Burger", - "description": "Heaven burger with your choice of cheese", - "price": 14.0 - }, - { - "title": "Chicken Caesar Wrap", - "description": "Tender grilled chicken, romaine lettuce, croutons, and Parmesan cheese tossed in a creamy Caesar dressing and wrapped in a spinach tortilla", - "price": 10.0 - }, - { - "title": "Kids Hot Dog", - "description": "Kids under 12", - "price": 5.0 - }, - { - "title": "Chicken Fingers", - "description": "Tender chicken strips, grilled or fried", - "price": 8.0 - } -] diff --git a/samples/js-menu/src/04/example.menuQuestion.json b/samples/js-menu/src/04/example.menuQuestion.json deleted file mode 100644 index 21c7c4647..000000000 --- a/samples/js-menu/src/04/example.menuQuestion.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "question": "I'd like something cheesy!" -} diff --git a/samples/js-menu/src/04/flows.ts b/samples/js-menu/src/04/flows.ts deleted file mode 100644 index c223dbe0e..000000000 --- a/samples/js-menu/src/04/flows.ts +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright 2024 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 { Document } from '@genkit-ai/ai/retriever'; -import { - devLocalIndexerRef, - devLocalRetrieverRef, -} from '@genkit-ai/dev-local-vectorstore'; -import { z } from 'genkit'; -import { ai } from '../genkit.js'; -import { - AnswerOutputSchema, - MenuItem, - MenuItemSchema, - MenuQuestionInputSchema, -} from '../types'; -import { s04_ragDataMenuPrompt } from './prompts'; - -// Define a flow which indexes items on the menu. - -export const s04_indexMenuItemsFlow = ai.defineFlow( - { - name: 's04_indexMenuItems', - inputSchema: z.array(MenuItemSchema), - outputSchema: z.object({ rows: z.number() }), - }, - async (menuItems) => { - // Store each document with its text indexed, - // and its original JSON data as its metadata. - const documents = menuItems.map((menuItem) => { - const text = `${menuItem.title} ${menuItem.price} \n ${menuItem.description}`; - return Document.fromText(text, menuItem); - }); - await ai.index({ - indexer: devLocalIndexerRef('menu-items'), - documents, - }); - return { rows: menuItems.length }; - } -); - -// Define a flow which generates a response to the question, -// by retrieving relevant items from the menu. -// View this flow's trace to see the context that was retrieved, -// and how it was included in the prompt. - -export const s04_ragMenuQuestionFlow = ai.defineFlow( - { - name: 's04_ragMenuQuestion', - inputSchema: MenuQuestionInputSchema, - outputSchema: AnswerOutputSchema, - }, - async (input) => { - // Retrieve the 3 most relevant menu items for the question - const docs = await ai.retrieve({ - retriever: devLocalRetrieverRef('menu-items'), - query: input.question, - options: { k: 3 }, - }); - const menuData: Array = docs.map( - (doc) => (doc.metadata || {}) as MenuItem - ); - - // Generate the response - const response = await s04_ragDataMenuPrompt({ - menuData: menuData, - question: input.question, - }); - return { answer: response.text }; - } -); diff --git a/samples/js-menu/src/04/prompts.ts b/samples/js-menu/src/04/prompts.ts deleted file mode 100644 index 7ed500a5f..000000000 --- a/samples/js-menu/src/04/prompts.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2024 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 { gemini15Flash } from '@genkit-ai/vertexai'; -import { ai } from '../genkit.js'; -import { DataMenuQuestionInputSchema } from '../types'; - -export const s04_ragDataMenuPrompt = ai.definePrompt( - { - name: 's04_ragDataMenu', - model: gemini15Flash, - input: { schema: DataMenuQuestionInputSchema }, - output: { format: 'text' }, - config: { temperature: 0.3 }, - }, - ` -You are acting as Walt, a helpful AI assistant here at the restaurant. -You can answer questions about the food on the menu or any other questions -customers have about food in general. - -Here are some items that are on today's menu that are relevant to -helping you answer the customer's question: -{{#each menuData~}} -- {{this.title}} \${{this.price}} - {{this.description}} -{{~/each}} - -Answer this customer's question: -{{question}}? -` -); diff --git a/samples/js-menu/src/05/example.visualMenuQuestion.json b/samples/js-menu/src/05/example.visualMenuQuestion.json deleted file mode 100644 index 3c49f36d0..000000000 --- a/samples/js-menu/src/05/example.visualMenuQuestion.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "question": "What kind of burger buns do you have?" -} diff --git a/samples/js-menu/src/05/flows.ts b/samples/js-menu/src/05/flows.ts deleted file mode 100644 index f884c9181..000000000 --- a/samples/js-menu/src/05/flows.ts +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright 2024 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 fs from 'fs'; -import { z } from 'genkit'; -import path from 'path'; -import { ai } from '../genkit.js'; -import { - AnswerOutputSchema, - MenuQuestionInputSchema, - TextMenuQuestionInputSchema, -} from '../types'; -import { s05_readMenuPrompt, s05_textMenuPrompt } from './prompts'; - -// Define a flow that takes an image, passes it to Gemini Vision Pro, -// and extracts all of the text from the photo of the menu. -// Note that this example uses a hard-coded image file, as image input -// is not currently available in the Development UI runners. - -export const s05_readMenuFlow = ai.defineFlow( - { - name: 's05_readMenuFlow', - inputSchema: z.void(), // input is data/menu.jpeg - outputSchema: z.object({ menuText: z.string() }), - }, - async (unused) => { - const imageDataUrl = await inlineDataUrl('menu.jpeg', 'image/jpeg'); - const response = await s05_readMenuPrompt({ - imageUrl: imageDataUrl, - }); - return { menuText: response.text }; - } -); - -// Define a flow which generates a response to the question. -// Just returns the llm's text response to the question. - -export const s05_textMenuQuestionFlow = ai.defineFlow( - { - name: 's05_textMenuQuestion', - inputSchema: TextMenuQuestionInputSchema, - outputSchema: AnswerOutputSchema, - }, - async (input) => { - const response = await s05_textMenuPrompt({ - menuText: input.menuText, - question: input.question, - }); - return { answer: response.text }; - } -); - -// Define a third composite flow which chains the first two flows - -export const s05_visionMenuQuestionFlow = ai.defineFlow( - { - name: 's05_visionMenuQuestion', - inputSchema: MenuQuestionInputSchema, - outputSchema: AnswerOutputSchema, - }, - async (input) => { - // Run the first flow to read the menu image. - const menuResult = await s05_readMenuFlow(); - - // Pass the text of the menu and the question to the second flow - // and return the answer as this output. - return s05_textMenuQuestionFlow({ - question: input.question, - menuText: menuResult.menuText, - }); - } -); - -// Helper to read a local file and inline it as a data url - -async function inlineDataUrl( - imageFilename: string, - contentType: string -): Promise { - const filePath = path.join('./data', imageFilename); - const imageData = fs.readFileSync(filePath); - return `data:${contentType};base64,${imageData.toString('base64')}`; -} diff --git a/samples/js-menu/src/05/prompts.ts b/samples/js-menu/src/05/prompts.ts deleted file mode 100644 index 894edd931..000000000 --- a/samples/js-menu/src/05/prompts.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2024 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 { gemini15Flash } from '@genkit-ai/vertexai'; -import { z } from 'genkit'; -import { ai } from '../genkit.js'; -import { TextMenuQuestionInputSchema } from '../types'; - -export const s05_readMenuPrompt = ai.definePrompt( - { - name: 's05_readMenu', - model: gemini15Flash, - input: { - schema: z.object({ - imageUrl: z.string(), - }), - }, - output: { format: 'text' }, - config: { temperature: 0.1 }, - }, - ` -Extract _all_ of the text, in order, -from the following image of a restaurant menu. - -{{media url=imageUrl}} -` -); - -export const s05_textMenuPrompt = ai.definePrompt( - { - name: 's05_textMenu', - model: gemini15Flash, - input: { schema: TextMenuQuestionInputSchema }, - output: { format: 'text' }, - config: { temperature: 0.3 }, - }, - ` -You are acting as Walt, a helpful AI assistant here at the restaurant. -You can answer questions about the food on the menu or any other questions -customers have about food in general. - -Here is the text of today's menu to help you answer the customer's question: -{{menuText}} - -Answer this customer's question: -{{question}}? -` -); diff --git a/samples/js-menu/src/genkit.ts b/samples/js-menu/src/genkit.ts deleted file mode 100644 index 6109f4b2a..000000000 --- a/samples/js-menu/src/genkit.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2024 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 { devLocalVectorstore } from '@genkit-ai/dev-local-vectorstore'; -import { textEmbedding004, vertexAI } from '@genkit-ai/vertexai'; -import { genkit } from 'genkit'; - -// Initialize Genkit - -export const ai = genkit({ - plugins: [ - vertexAI({ location: 'us-central1' }), - devLocalVectorstore([ - { - indexName: 'menu-items', - embedder: textEmbedding004, - embedderOptions: { taskType: 'RETRIEVAL_DOCUMENT' }, - }, - ]), - ], -}); diff --git a/samples/js-menu/src/index.ts b/samples/js-menu/src/index.ts deleted file mode 100644 index 5776c2268..000000000 --- a/samples/js-menu/src/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2024 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. - */ - -// Export all of the example prompts and flows - -// 01 -export { s01_staticMenuDotPrompt, s01_vanillaPrompt } from './01/prompts'; -// 02 -export { s02_menuQuestionFlow } from './02/flows'; -export { s02_dataMenuPrompt } from './02/prompts'; -// 03 -export { s03_multiTurnChatFlow } from './03/flows'; -export { s03_chatPreamblePrompt } from './03/prompts'; -// 04 -export { s04_indexMenuItemsFlow, s04_ragMenuQuestionFlow } from './04/flows'; -export { s04_ragDataMenuPrompt } from './04/prompts'; -// 05 -export { - s05_readMenuFlow, - s05_textMenuQuestionFlow, - s05_visionMenuQuestionFlow, -} from './05/flows'; -export { s05_readMenuPrompt, s05_textMenuPrompt } from './05/prompts'; diff --git a/samples/js-menu/src/types.ts b/samples/js-menu/src/types.ts deleted file mode 100644 index efe7bbc99..000000000 --- a/samples/js-menu/src/types.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2024 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 * as z from 'zod'; - -// The data model for a restaurant menu - -export const MenuItemSchema = z.object({ - title: z.string().describe('The name of the menu item'), - description: z - .string() - .describe('Details including ingredients and preparation'), - price: z.number().describe('Price in dollars'), -}); - -export type MenuItem = z.infer; - -// Input schema for a question about the menu - -export const MenuQuestionInputSchema = z.object({ - question: z.string(), -}); - -// Output schema containing an answer to a question - -export const AnswerOutputSchema = z.object({ - answer: z.string(), -}); - -// Input schema for a question about the menu -// where the menu is provided in JSON data. - -export const DataMenuQuestionInputSchema = z.object({ - menuData: z.array(MenuItemSchema), - question: z.string(), -}); - -// Input schema for a question about the menu -// where the menu is provided as unstructured text. - -export const TextMenuQuestionInputSchema = z.object({ - menuText: z.string(), - question: z.string(), -}); - -// Also export Typescript types for each of these Zod schemas -export type MenuQuestionInput = z.infer; -export type AnswerOutput = z.infer; -export type DataMenuPromptInput = z.infer; -export type TextMenuQuestionInput = z.infer; diff --git a/samples/js-menu/tsconfig.json b/samples/js-menu/tsconfig.json deleted file mode 100644 index e51f33ae3..000000000 --- a/samples/js-menu/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "module": "NodeNext", - "noImplicitReturns": true, - "noUnusedLocals": false, - "outDir": "lib", - "sourceMap": true, - "strict": true, - "target": "es2017", - "skipLibCheck": true, - "esModuleInterop": true - }, - "compileOnSave": true, - "include": ["src"] -} diff --git a/samples/js-schoolAgent/README.md b/samples/js-schoolAgent/README.md deleted file mode 100644 index 35d91f3ab..000000000 --- a/samples/js-schoolAgent/README.md +++ /dev/null @@ -1,107 +0,0 @@ -# School Agent Sample - -A demonstration of a conversational, multi-agent assistant for a school system using GenKit and Google's Gemini Pro. This agent helps parents with attendance reporting and school information queries. - -In this example we have a RoutingAgent which is the main, general-purpose agent. -This agent comes equipped with additional specialized agents, that it can hand-off to as needed. - -These specialized agents are represented as prompts and embedded as tools to the original agent. - -## Agent Tools & Capabilities - -- **Agent Structure**: - - `RoutingAgent`: Main entry point and router, handling general queries and delegating to specialized agents - - `AttendanceAgent`: Specialized agent for absence/tardy reporting - - `GradesAgent`: Manages grade-related inquiries and academic performance - -Each specialized agent has its own set of tools that are only accessible to that specific agent: - -- **AttendanceAgent**: - - `reportAbsence`: Submit absence notifications - - `reportTardy`: Report late arrivals -- **GradesAgent**: - - `getRecentGrades`: Retrieve latest grade information - -The main RoutingAgent cannot directly access these specialized tools - it can only access its own tools and delegate to the specialized agents. This means the specialized agent descriptions need to clearly communicate their capabilities, since the main agent relies on these descriptions for appropriate routing. - -For example, when the RoutingAgent sees a grade-related query, it needs to know from the GradesAgent's description that it can handle grade lookups, even though it can't directly see the `getRecentGrades` tool. - -This architectural pattern: - -- Maintains clear separation of concerns -- Allows specialized agents to evolve independently -- Allows scaling up to a larger number of tools - -NOTE: The agent description is how the generalized agent knows what tools the specialized agent has available. An agent description that is too general may cause the routing agent to mess up by not knowing that a certain functionality was actually available. - -## Prerequisites - -- Node.js and genkit CLI installed -- Google AI API key - -## Getting Started - -1. Install dependencies: - -```bash -npm install -``` - -2. Set up your Google AI API key: - -```bash -export GOOGLE_GENAI_API_KEY=your_api_key_here -``` - -3. Start the development server: - -```bash -npm run genkit:dev -``` - -In your terminal, a commandline chat interface should show up: - -``` -Telemetry API running on http://localhost:4033 -Genkit Developer UI: http://localhost:4000 - -> school-agent@1.0.0 dev -> tsx --no-warnings --watch src/terminal.ts - -bell> Hi there, my name is Bell and I'm here to help! 👋🎉 I'm your friendly AI assistant for parents of Sparkyville High School. I can answer your questions about the school, events, grades, and more. Just ask me! 😊 - -prompt> [insert your chats here] -``` - -You can feel free to tweak the sample. The project builds in watch mode, so any changes will be picked up immediately and should restart the conversation. - -## Usage - -The agent uses a multi-agent architecture: - -- Routing Agent: Acts as the main entry point and router, handling general queries while delegating specialized requests to appropriate agents -- Attendance Agent: Specialized agent focused on absence and tardy reporting -- Grades Agent: Manages academic performance queries, grade reports, and transcript requests - -Example queries: - -- "Evelyn will be late today" -- "What are the upcoming holidays I should be aware of?" -- "Show me my child's current grades" - -## Development - -- `npm run dev` - Run in development mode with hot reloading -- `npm run build` - Build the project -- `npm start` - Run the built version - -## Project Structure - -- `src/` - - `agents/` - - `routingAgent.ts` - Main agent that uses other agents as tools - - `attendanceAgent.ts` - Specialized attendance agent - - `gradesAgent.ts` - Academic performance and grades agent - - `tools.ts` - Tool definitions - - `types.ts` - TypeScript types - - `data.ts` - Sample data diff --git a/samples/js-schoolAgent/package.json b/samples/js-schoolAgent/package.json deleted file mode 100644 index 3ebc1299b..000000000 --- a/samples/js-schoolAgent/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "school-agent", - "version": "1.0.0", - "description": "", - "main": "lib/index.js", - "scripts": { - "start": "node lib/terminal.js", - "dev": "tsx --no-warnings --watch src/terminal.ts", - "genkit:dev": "genkit start -- npm run dev", - "compile": "tsc", - "build": "pnpm build:clean && npm run compile", - "build:clean": "rimraf ./lib", - "build:watch": "tsc --watch" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "genkit": "^0.9.3", - "@genkit-ai/googleai": "^0.9.3", - "google-auth-library": "^9.6.3", - "llm-chunk": "^0.0.1", - "pdf-parse": "^1.1.1" - }, - "devDependencies": { - "genkit-cli": "^0.9.3", - "@types/pdf-parse": "^1.1.4", - "cross-env": "^7.0.3", - "rimraf": "^6.0.1", - "tsx": "^4.19.1", - "typescript": "^5.3.3" - } -} diff --git a/samples/js-schoolAgent/prompts/myPrompt.prompt b/samples/js-schoolAgent/prompts/myPrompt.prompt deleted file mode 100644 index c6ffa8db4..000000000 --- a/samples/js-schoolAgent/prompts/myPrompt.prompt +++ /dev/null @@ -1 +0,0 @@ -{{ role "system" }} your name is {{ @state.userName }}, always introduce yourself) \ No newline at end of file diff --git a/samples/js-schoolAgent/src/attendanceAgent.ts b/samples/js-schoolAgent/src/attendanceAgent.ts deleted file mode 100644 index 044aefd7c..000000000 --- a/samples/js-schoolAgent/src/attendanceAgent.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2024 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 { ai } from './genkit.js'; -import { reportAbsence, reportTardy } from './tools.js'; -import { agentDescription } from './util.js'; - -const tools = [reportAbsence, reportTardy, 'routingAgent']; -const specialization = 'attendance'; - -const toolNames: string[] = tools.map((item) => { - if (typeof item === 'string') { - return item; - } else { - return item.name; - } -}); - -export const attendanceAgent = ai.definePrompt( - { - name: `${specialization}Agent`, - description: agentDescription(specialization, toolNames), - tools, - }, - ` {{ role "system"}} - -You are Bell, a helpful attendance assistance agent for Sparkyville High School. -A parent has been referred to you to handle a ${specialization}-related concern. -Use the tools available to you to assist the parent. - -- Parents may only report absences for their own students. -- If you are unclear about any of the fields required to report an absence or tardy, request clarification before using the tool. -- If the parent asks about anything other than attendance-related concerns that you can handle, transfer to the info agent. - - {{ userContext @state }} - ` -); diff --git a/samples/js-schoolAgent/src/data.ts b/samples/js-schoolAgent/src/data.ts deleted file mode 100644 index b44268d76..000000000 --- a/samples/js-schoolAgent/src/data.ts +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright 2024 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. - */ - -export async function getUpcomingHolidays() { - return [ - { date: '2024-11-28', holiday: 'Thanksgiving Break' }, - { date: '2024-11-29', holiday: 'Thanksgiving Break (Day 2)' }, - ]; -} - -export const EXAMPLE_EVENTS = [ - { - event: 'Freshman Fall Concert', - activity: 'Choir', - location: 'School Auditorium', - startTime: '2024-11-12T19:00', - endTime: '2023-11-12T20:30', - grades: [9], - }, - { - event: 'Fall Pep Rally', - location: 'Football Field', - startTime: '2024-10-27T14:00', - endTime: '2024-10-27T15:30', - grades: [9, 10, 11, 12], - }, - { - event: 'Junior Fall Concert', - activity: 'Choir', - location: 'School Auditorium', - startTime: '2024-11-15T19:00', - endTime: '2024-11-15T20:30', - grades: [11], - }, - { - event: 'Varsity Chess Club Tournament', - activity: 'Chess Club', - location: 'Library', - startTime: '2024-11-04T16:00', - endTime: '2024-11-04T18:00', - grades: [11, 12], - }, - { - event: 'Drama Club Auditions', - activity: 'Drama Club', - location: 'School Auditorium', - startTime: '2024-10-20T15:00', - endTime: '2024-10-20T17:00', - grades: [9, 10, 11, 12], - }, -]; - -export interface GradeEntry { - studentId: number; - subject: string; - grade: string; - date: string; - assignment: string; -} - -export const EXAMPLE_GRADES: GradeEntry[] = [ - { - studentId: 3734, - subject: 'Mathematics', - grade: 'A-', - date: '2024-03-01', - assignment: 'Quadratic Equations Quiz', - }, - { - studentId: 3734, - subject: 'English', - grade: 'B+', - date: '2024-03-05', - assignment: 'Essay: Shakespeare Analysis', - }, - { - studentId: 9433, - subject: 'Physics', - grade: 'A', - date: '2024-03-02', - assignment: 'Forces Lab Report', - }, -]; diff --git a/samples/js-schoolAgent/src/genkit.ts b/samples/js-schoolAgent/src/genkit.ts deleted file mode 100644 index 025c4b30d..000000000 --- a/samples/js-schoolAgent/src/genkit.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2024 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 { gemini15Pro, googleAI } from '@genkit-ai/googleai'; -import { genkit } from 'genkit'; -import { AgentState } from './types'; - -export const ai = genkit({ - plugins: [googleAI()], - model: gemini15Pro, -}); - -ai.defineHelper( - 'userContext', - (state: AgentState) => `=== User Context - -- The current parent user is ${state?.parentName} -- The current date and time is: ${new Date().toString()} - -=== Registered students of the current user - -${state?.students.map((s) => ` - ${s.name}, student id: ${s.id} grade: ${s.grade}, activities: \n${s.activities.map((a) => ` - ${a}`).join('\n')}`).join('\n\n')}` -); - -export { z } from 'genkit'; diff --git a/samples/js-schoolAgent/src/gradesAgent.ts b/samples/js-schoolAgent/src/gradesAgent.ts deleted file mode 100644 index 58871fc52..000000000 --- a/samples/js-schoolAgent/src/gradesAgent.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2024 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 { ai } from './genkit.js'; -import { getRecentGrades } from './tools.js'; -import { agentDescription } from './util.js'; - -const tools = [getRecentGrades, 'routingAgent']; -const specialization = 'grades'; - -const toolNames: string[] = tools.map((item) => { - if (typeof item === 'string') { - return item; - } else { - return item.name; - } -}); - -export const gradesAgent = ai.definePrompt( - { - name: `${specialization}Agent`, - description: agentDescription(specialization, toolNames), - tools, - }, - ` {{ role "system"}} - -You are Bell, a helpful attendance assistance agent for Sparkyville High School. -A parent has been referred to you to handle a ${specialization}-related concern. -Use the tools available to you to assist the parent. - -Guidelines: -- Parents may only view grades for their own students -- Always verify the student belongs to the parent before sharing grade information -- Be encouraging and positive when discussing grades -- If asked about non-grade related topics, transfer back to the info agent -- Format grade information in a clear, easy-to-read manner - -{{ userContext @state }} - ` -); diff --git a/samples/js-schoolAgent/src/routingAgent.ts b/samples/js-schoolAgent/src/routingAgent.ts deleted file mode 100644 index 3986baa4f..000000000 --- a/samples/js-schoolAgent/src/routingAgent.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2024 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 { attendanceAgent } from './attendanceAgent'; -import { ai } from './genkit'; -import { gradesAgent } from './gradesAgent'; -import { searchEvents, upcomingHolidays } from './tools.js'; - -export const routingAgent = ai.definePrompt( - { - name: 'routingAgent', - description: `This agent helps with answering inquiries and requests.`, - tools: [searchEvents, attendanceAgent, gradesAgent, upcomingHolidays], - }, - `You are Bell, the friendly AI office receptionist at Sparkyville High School. - - Your job is to help answer inquiries from parents. Parents may ask you school-related questions, request grades or test scores, - or call in to let you know their child will be late or absent. - - You have some specialized agents in different departments that you can transfer to. - - 1. Grades Agent - This agent can provide informtion about previous scores for assignments and tests. - 2. Attendance Agent - This agent can help with attendance requests, such as marking a student as late/tardy or absent. - - Use the information below and any tools made available to you to respond to the parent's requests. - - If the parent has an inquiry that you do not know the answer to, do NOT make the answer up. Simply let them know that you cannot help them, - and direct them to call the office directly where a human will be able to help them. - - === Frequently Asked Questions - - - Classes begin at 8am, students are dismissed at 3:30pm - - Parking permits are issued on a first-come first-serve basis beginning Aug 1 - - {{userContext @state }} -` -); diff --git a/samples/js-schoolAgent/src/terminal.ts b/samples/js-schoolAgent/src/terminal.ts deleted file mode 100644 index 9b3717449..000000000 --- a/samples/js-schoolAgent/src/terminal.ts +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright 2024 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 { Message, ToolRequestPart } from 'genkit'; -import { createInterface } from 'node:readline'; -import { ai } from './genkit.js'; -import { routingAgent } from './routingAgent.js'; - -const rl = createInterface({ - input: process.stdin, - output: process.stdout, -}); - -const EXAMPLE_USER_CONTEXT = { - parentId: 4112, - parentName: 'Francis Smith', - students: [ - { - id: 3734, - name: 'Evelyn Smith', - grade: 9, - activities: ['Choir', 'Drama Club'], - }, - { id: 9433, name: 'Evan Smith', grade: 11, activities: ['Chess Club'] }, - ], -}; - -// ANSI color codes for terminal output -const COLORS = { - BELL: '\x1b[33m', - PROMPT: '\x1b[36m', - RESET: '\x1b[0m', -}; - -// Helper to print colored text -function printColored(prefix: string, text: string, color: string) { - console.log(`${color}${prefix}>${COLORS.RESET}`, text); -} - -// Get initial greeting from AI -async function getGreeting() { - const { text } = await ai.generate( - 'Come up with a short friendly greeting for yourself talking to a parent as Bell, the helpful AI assistant for parents of Sparkyville High School. Feel free to use emoji.' - ); - return text; -} - -// Process and display the chat response stream -async function handleChatResponse( - stream: AsyncIterable<{ text: string }>, - response: Promise, - startMessageCount: number -) { - console.log(); - process.stdout.write(`${COLORS.BELL}bell>${COLORS.RESET} `); - - for await (const chunk of stream) { - process.stdout.write(chunk.text); - } - - // Extract and display tools used - const toolsUsed = (await response).messages - .slice(startMessageCount) - .filter((m: Message) => m.role === 'model') - .map((m: Message) => - m.content - .filter((p) => !!p.toolRequest) - .map( - (p) => - `${p.toolRequest?.name}(${JSON.stringify(p.toolRequest?.input)})` - ) - ) - .flat() - .filter((t: ToolRequestPart) => !!t); - - console.log('\nTools Used:', toolsUsed); -} - -// Main chat loop -async function handleUserInput(chat: any): Promise { - return new Promise((resolve) => { - rl.question(`\n${COLORS.PROMPT}prompt>${COLORS.RESET} `, async (input) => { - try { - const startMessageCount = chat.messages.length; - const { stream, response } = await chat.sendStream(input); - await handleChatResponse(stream, response, startMessageCount); - resolve(); - } catch (e) { - console.log('Error:', e); - resolve(); - } - }); - }); -} - -async function main() { - const chat = ai - .createSession({ initialState: EXAMPLE_USER_CONTEXT }) - .chat(routingAgent); - - const greeting = await getGreeting(); - console.log(); - printColored('bell', greeting, COLORS.BELL); - - while (true) { - await handleUserInput(chat); - } -} - -setTimeout(main, 0); diff --git a/samples/js-schoolAgent/src/tools.ts b/samples/js-schoolAgent/src/tools.ts deleted file mode 100644 index b0e3b848b..000000000 --- a/samples/js-schoolAgent/src/tools.ts +++ /dev/null @@ -1,147 +0,0 @@ -/** - * Copyright 2024 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 { EXAMPLE_EVENTS, EXAMPLE_GRADES, getUpcomingHolidays } from './data.js'; -import { ai, z } from './genkit.js'; -import { AgentState } from './types.js'; - -export const searchEvents = ai.defineTool( - { - name: 'searchEvents', - description: - 'use this when asked about any time/location for school events including extra curriculars like clubs', - inputSchema: z.object({ - activity: z - .string() - .optional() - .describe( - 'if looking for a particular activity, provide it here. must be an exact match for an activity name' - ), - grade: z - .number() - .optional() - .describe('restrict searched events to a particular grade level'), - }), - }, - async ({ activity, grade }) => { - return EXAMPLE_EVENTS.filter( - (e) => !grade || e.grades.includes(grade) - ).filter( - (e) => !activity || e.activity?.toLowerCase() === activity?.toLowerCase() - ); - } -); - -function checkIsParent(studentId: number, state: AgentState) { - const student = state.students.find((s) => s.id === studentId); - if (!student) { - throw new Error( - `Unable to process request for student ID ${studentId}. Parents can only submit requests for their registered children.` - ); - } - return student; -} - -export const reportAbsence = ai.defineTool( - { - name: 'reportAbsence', - description: - 'use this tool to mark a specific student as absent on one or more days', - inputSchema: z.object({ - studentId: z.number().describe('the id of the student'), - date: z.string().describe('the date of the absence in YYYY-MM-DD format'), - reason: z.string().describe('the provided reason for the absence'), - excused: z - .boolean() - .describe('whether the absence is excused by the parent'), - }), - }, - async (input) => { - const student = checkIsParent( - input.studentId, - ai.currentSession().state! - ); - console.log( - `[TOOL] Absence reported for ${student.name} (ID: ${input.studentId}) on ${input.date}` - ); - return { ok: true, message: 'Absence successfully recorded' }; - } -); - -export const reportTardy = ai.defineTool( - { - name: 'reportTardy', - description: - 'use this tool to mark a specific student tardy for a given date', - inputSchema: z.object({ - studentId: z.number().describe('the id of the student'), - date: z.string().describe('the date of the tardy'), - reason: z.string().describe('the provided reason reason for the tardy'), - eta: z - .string() - .describe( - 'the time the student is expected to arrive at school in HH:MMam/pm format' - ), - excused: z - .boolean() - .describe('whether the absense is excused by the parent'), - }), - }, - async (input) => { - checkIsParent(input.studentId, ai.currentSession().state!); - console.log( - '[TOOL] Student', - input.studentId, - 'has been reported tardy for', - input.date - ); - return { ok: true }; - } -); - -export const upcomingHolidays = ai.defineTool( - { - name: 'upcomingHolidays', - description: 'can retrieve information about upcoming holidays', - outputSchema: z.string(), - }, - async () => JSON.stringify(await getUpcomingHolidays()) -); - -export const getRecentGrades = ai.defineTool( - { - name: 'getRecentGrades', - description: 'retrieves recent grades for a specific student', - inputSchema: z.object({ - studentId: z.number().describe('the id of the student'), - subject: z.string().optional().describe('optional subject filter'), - limit: z - .number() - .optional() - .describe('number of recent grades to return'), - }), - }, - async ({ studentId, subject, limit = 5 }) => { - checkIsParent(studentId, ai.currentSession().state!); - let grades = EXAMPLE_GRADES.filter((g) => g.studentId === studentId); - if (subject) { - grades = grades.filter( - (g) => g.subject.toLowerCase() === subject.toLowerCase() - ); - } - return grades.slice(0, limit); - } -); diff --git a/samples/js-schoolAgent/src/types.ts b/samples/js-schoolAgent/src/types.ts deleted file mode 100644 index 542b2351f..000000000 --- a/samples/js-schoolAgent/src/types.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright 2024 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. - */ - -export interface AgentState { - parentId: number; - parentName: string; - students: { - id: number; - name: string; - grade: number; - activities: string[]; - }[]; -} diff --git a/samples/js-schoolAgent/src/util.ts b/samples/js-schoolAgent/src/util.ts deleted file mode 100644 index e66f628fe..000000000 --- a/samples/js-schoolAgent/src/util.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright 2024 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. - */ - -export const agentDescription = (specialization: string, tools: string[]) => ` -Transfer to this agent when the user asks about ${specialization}. -This agent can perform the following functions: ${tools.map((t) => t).join(', ')}. -Do not mention that you are transferring, just do it.`; diff --git a/samples/js-schoolAgent/tsconfig.json b/samples/js-schoolAgent/tsconfig.json deleted file mode 100644 index b73ccd04d..000000000 --- a/samples/js-schoolAgent/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "module": "NodeNext", - "moduleResolution": "NodeNext", - "noImplicitReturns": true, - "noUnusedLocals": false, - "outDir": "lib", - "sourceMap": true, - "strict": true, - "target": "es2017", - "skipLibCheck": true, - "esModuleInterop": true - }, - "compileOnSave": true, - "include": ["src"] -} diff --git a/samples/prompts/README.md b/samples/prompts/README.md deleted file mode 100644 index 35f1a1735..000000000 --- a/samples/prompts/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Prompts Samples - -These sample shows off several of the prompting techniques show in [docs/prompts.md](/docs/prompts.md) - -These examples use Google Gemini, set your API key in the `GOOGLE_GENAI_API_KEY` environment variable. - -You can run these prompts in the Developer UI with `genkit start` - -or invoke them with e.g. `genkit flow:run simplePrompt` diff --git a/samples/prompts/package.json b/samples/prompts/package.json deleted file mode 100644 index 081931c36..000000000 --- a/samples/prompts/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "prompts", - "version": "1.0.0", - "description": "", - "main": "lib/index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node lib/index.js", - "genkit:dev": "genkit start -- tsx --watch src/index.ts", - "build": "tsc", - "build:watch": "tsc --watch" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "genkit": "^0.9.0-rc || ^0.9", - "@genkit-ai/googleai": "^0.9.0-rc || ^0.9", - "express": "^4.21.0", - "zod": "^3.23.8" - }, - "devDependencies": { - "genkit-cli": "^0.9.0-rc || ^0.9", - "typescript": "^5.5.4" - } -} diff --git a/samples/prompts/src/index.ts b/samples/prompts/src/index.ts deleted file mode 100644 index 9149c0358..000000000 --- a/samples/prompts/src/index.ts +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright 2024 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 the Genkit core libraries and plugins. -import { googleAI } from '@genkit-ai/googleai'; -import { genkit, z } from 'genkit'; - -const ai = genkit({ - plugins: [ - googleAI(), // Provide the key via the GOOGLE_GENAI_API_KEY environment variable or arg { apiKey: 'yourkey'} - ], -}); - -const simplePrompt = ai.defineFlow('simplePrompt', () => - ai.generate({ - model: 'googleai/gemini-1.5-flash', - prompt: 'You are a helpful AI assistant named Walt, say hello', - }) -); - -const simpleTemplate = ai.defineFlow('simpleTemplate', () => { - const name = 'Fred'; - return ai.generate({ - model: 'googleai/gemini-1.5-flash', - prompt: `You are a helpful AI assistant named Walt. Say hello to ${name}.`, - }); -}); - -const helloDotprompt = ai.definePrompt( - { - name: 'helloPrompt', - model: 'googleai/gemini-1.5-flash', - input: { - schema: z.object({ name: z.string() }), - }, - }, - `You are a helpful AI assistant named Walt. Say hello to {{name}}` -); - -const simpleDotprompt = ai.defineFlow('simpleDotprompt', () => { - return helloDotprompt({ name: 'Fred' }); -}); - -const outputSchema = z.object({ - short: z.string(), - friendly: z.string(), - likeAPirate: z.string(), -}); - -const threeGreetingsPrompt = ai.definePrompt( - { - name: 'threeGreetingsPrompt', - model: 'googleai/gemini-1.5-flash', - input: { - schema: z.object({ name: z.string() }), - }, - output: { - format: 'json', - schema: outputSchema, - }, - }, - `You are a helpful AI assistant named Walt. Say hello to {{name}}, write a response for each of the styles requested` -); - -const threeGreetings = ai.defineFlow('threeGreetingsPrompt', async () => { - const response = await threeGreetingsPrompt({ name: 'Fred' }); - return response.output?.likeAPirate; -}); - -// Start a flow server, which exposes your flows as HTTP endpoints. This call -// must come last, after all of your plug-in configuration and flow definitions. -// You can optionally specify a subset of flows to serve, and configure some -// HTTP server options, but by default, the flow server serves all defined flows. -ai.startFlowServer({ - flows: [threeGreetings, simpleTemplate, simpleDotprompt, simplePrompt], -}); diff --git a/samples/prompts/tsconfig.json b/samples/prompts/tsconfig.json deleted file mode 100644 index efbb566bf..000000000 --- a/samples/prompts/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compileOnSave": true, - "include": ["src"], - "compilerOptions": { - "module": "commonjs", - "noImplicitReturns": true, - "outDir": "lib", - "sourceMap": true, - "strict": true, - "target": "es2017", - "skipLibCheck": true, - "esModuleInterop": true - } -} From 70328e4878141775983897603749ae21c448bc00 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 19 Dec 2024 12:13:16 -0500 Subject: [PATCH 142/562] core: fixed sample code snippet for genkit/client (#1537) --- js/genkit/src/client/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/src/client/client.ts b/js/genkit/src/client/client.ts index 9f5bc9e84..d9144e1e1 100644 --- a/js/genkit/src/client/client.ts +++ b/js/genkit/src/client/client.ts @@ -22,7 +22,7 @@ const __flowStreamDelimiter = '\n\n'; * For example: * * ```js - * import { streamFlow } from '@genkit-ai/core/flow-client'; + * import { streamFlow } from 'genkit/client'; * * const response = streamFlow({ * url: 'https://my-flow-deployed-url', From 4c89f5f05d5bb94b5f51b694a686b3d0aa5d9c91 Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:22:58 +0000 Subject: [PATCH 143/562] feat: support jsonl in evals cli (#1541) --- genkit-tools/cli/src/commands/eval-flow.ts | 14 +++- genkit-tools/cli/src/commands/eval-run.ts | 8 +- genkit-tools/common/src/types/eval.ts | 20 +++-- genkit-tools/common/src/utils/eval.ts | 77 +++++++++++++++++++ .../evals/data/cat_adoption_questions.json | 14 ---- .../evals/data/cat_adoption_questions.jsonl | 4 + 6 files changed, 108 insertions(+), 29 deletions(-) delete mode 100644 js/testapps/evals/data/cat_adoption_questions.json create mode 100644 js/testapps/evals/data/cat_adoption_questions.jsonl diff --git a/genkit-tools/cli/src/commands/eval-flow.ts b/genkit-tools/cli/src/commands/eval-flow.ts index 9bf563e65..f3f58765b 100644 --- a/genkit-tools/cli/src/commands/eval-flow.ts +++ b/genkit-tools/cli/src/commands/eval-flow.ts @@ -28,9 +28,12 @@ import { runEvaluation, runInference, } from '@genkit-ai/tools-common/eval'; -import { confirmLlmUse, logger } from '@genkit-ai/tools-common/utils'; +import { + confirmLlmUse, + loadEvalInference, + logger, +} from '@genkit-ai/tools-common/utils'; import { Command } from 'commander'; -import { readFile } from 'fs/promises'; import { runWithManager } from '../utils/manager-utils'; interface EvalFlowRunCliOptions { @@ -172,8 +175,11 @@ async function readInputs( parsedData = JSON.parse(dataField!); break; case SourceType.FILE: - parsedData = JSON.parse(await readFile(input!, 'utf8')); - break; + try { + return await loadEvalInference(input!); + } catch (e) { + throw new Error(`Error parsing the input from file. Error: ${e}`); + } case SourceType.DATASET: const datasetStore = await getDatasetStore(); const data = await datasetStore.getDataset(input!); diff --git a/genkit-tools/cli/src/commands/eval-run.ts b/genkit-tools/cli/src/commands/eval-run.ts index b7e33fbb1..48f3719c2 100644 --- a/genkit-tools/cli/src/commands/eval-run.ts +++ b/genkit-tools/cli/src/commands/eval-run.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Action, EvalInput } from '@genkit-ai/tools-common'; +import { Action, EvalInputDataset } from '@genkit-ai/tools-common'; import { EvalExporter, getAllEvaluatorActions, @@ -25,10 +25,10 @@ import { import { confirmLlmUse, generateTestCaseId, + loadEvalInputDataset, logger, } from '@genkit-ai/tools-common/utils'; import { Command } from 'commander'; -import { readFile } from 'fs/promises'; import { runWithManager } from '../utils/manager-utils'; interface EvalRunCliOptions { @@ -92,8 +92,8 @@ export const evalRun = new Command('eval:run') } } - const evalDataset: EvalInput[] = JSON.parse( - (await readFile(dataset)).toString('utf-8') + const evalDataset: EvalInputDataset = ( + await loadEvalInputDataset(dataset) ).map((testCase: any) => ({ ...testCase, testCaseId: testCase.testCaseId || generateTestCaseId(), diff --git a/genkit-tools/common/src/types/eval.ts b/genkit-tools/common/src/types/eval.ts index f9c1ed867..eed656619 100644 --- a/genkit-tools/common/src/types/eval.ts +++ b/genkit-tools/common/src/types/eval.ts @@ -45,18 +45,21 @@ export const ModelInferenceInputJSONSchema = zodToJsonSchema( } ) as JSONSchema7; +/** + * A single sample to be used for inference. + **/ +export const EvalInferenceSampleSchema = z.object({ + testCaseId: z.string().optional(), + input: z.any(), + reference: z.any().optional(), +}); + /** * A set of samples that is ready for inference. * * This should be used in user-facing surfaces (CLI/API inputs) to accommodate various user input formats. For internal wire-transfer/storage, prefer {@link Dataset}. */ -export const EvalInferenceInputSchema = z.array( - z.object({ - testCaseId: z.string().optional(), - input: z.any(), - reference: z.any().optional(), - }) -); +export const EvalInferenceInputSchema = z.array(EvalInferenceSampleSchema); export type EvalInferenceInput = z.infer; /** @@ -87,6 +90,9 @@ export const EvalInputSchema = z.object({ }); export type EvalInput = z.infer; +export const EvalInputDatasetSchema = z.array(EvalInputSchema); +export type EvalInputDataset = z.infer; + export const EvalMetricSchema = z.object({ evaluator: z.string(), scoreId: z.string().optional(), diff --git a/genkit-tools/common/src/utils/eval.ts b/genkit-tools/common/src/utils/eval.ts index 87204db04..08023fc04 100644 --- a/genkit-tools/common/src/utils/eval.ts +++ b/genkit-tools/common/src/utils/eval.ts @@ -15,7 +15,10 @@ */ import { randomUUID } from 'crypto'; +import { createReadStream } from 'fs'; +import { readFile } from 'fs/promises'; import * as inquirer from 'inquirer'; +import { createInterface } from 'readline'; import { EvalField, EvaluationExtractor, @@ -25,6 +28,14 @@ import { findToolsConfig, isEvalField, } from '../plugin'; +import { + EvalInferenceInput, + EvalInferenceInputSchema, + EvalInferenceSampleSchema, + EvalInputDataset, + EvalInputDatasetSchema, + EvalInputSchema, +} from '../types'; import { Action } from '../types/action'; import { DocumentData, RetrieverResponse } from '../types/retrievers'; import { NestedSpanData, TraceData } from '../types/trace'; @@ -222,3 +233,69 @@ export async function getEvalExtractors( export function generateTestCaseId() { return randomUUID(); } + +/** Load a {@link EvalInferenceInput} file. Supports JSON / JSONL */ +export async function loadEvalInference( + fileName: string +): Promise { + const isJsonl = fileName.endsWith('.jsonl'); + + if (isJsonl) { + return await readJsonlForInference(fileName); + } else { + const parsedData = JSON.parse(await readFile(fileName, 'utf8')); + return EvalInferenceInputSchema.parse(parsedData); + } +} + +/** Load a {@link Eval} file. Supports JSON / JSONL */ +export async function loadEvalInputDataset( + fileName: string +): Promise { + const isJsonl = fileName.endsWith('.jsonl'); + + if (isJsonl) { + return await readJsonlForEvaluation(fileName); + } else { + const parsedData = JSON.parse(await readFile(fileName, 'utf8')); + return EvalInputDatasetSchema.parse(parsedData); + } +} + +async function readJsonlForInference( + fileName: string +): Promise { + const lines = await readLines(fileName); + const samples: EvalInferenceInput = []; + for (const line of lines) { + const parsedSample = EvalInferenceSampleSchema.parse(JSON.parse(line)); + samples.push(parsedSample); + } + return samples; +} + +async function readJsonlForEvaluation( + fileName: string +): Promise { + const lines = await readLines(fileName); + const inputs: EvalInputDataset = []; + for (const line of lines) { + const parsedSample = EvalInputSchema.parse(JSON.parse(line)); + inputs.push(parsedSample); + } + return inputs; +} + +async function readLines(fileName: string): Promise { + const lines: string[] = []; + const fileStream = createReadStream(fileName); + const rl = createInterface({ + input: fileStream, + crlfDelay: Infinity, + }); + + for await (const line of rl) { + lines.push(line); + } + return lines; +} diff --git a/js/testapps/evals/data/cat_adoption_questions.json b/js/testapps/evals/data/cat_adoption_questions.json deleted file mode 100644 index e9d37bb59..000000000 --- a/js/testapps/evals/data/cat_adoption_questions.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "input": "What are typical cat behaviors?" - }, - { - "input": "What supplies do you need when bringing home a new cat?" - }, - { - "input": "How often should you trim your cat's nails?" - }, - { - "input": "What are some plants that are toxic to cats?" - } -] diff --git a/js/testapps/evals/data/cat_adoption_questions.jsonl b/js/testapps/evals/data/cat_adoption_questions.jsonl new file mode 100644 index 000000000..376e76998 --- /dev/null +++ b/js/testapps/evals/data/cat_adoption_questions.jsonl @@ -0,0 +1,4 @@ +{"input":"What are typical cat behaviors?"} +{"input":"What supplies do you need when bringing home a new cat?"} +{"input":"How often should you trim your cat's nails?"} +{"input":"What are some plants that are toxic to cats?"} From a9e63ed0a43524e1db4ad4f2069582d29553ff1e Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 19 Dec 2024 13:57:32 -0500 Subject: [PATCH 144/562] fix(js): make sure middleware is applied by prompts (#1534) --- js/ai/src/prompt.ts | 9 +- js/genkit/src/genkit.ts | 18 +- js/genkit/tests/prompts_test.ts | 244 +++++++++++++++++++++- js/plugins/dotprompt/src/index.ts | 6 +- js/plugins/dotprompt/src/metadata.ts | 4 + js/plugins/dotprompt/src/prompt.ts | 15 +- js/plugins/dotprompt/tests/prompt_test.ts | 8 +- 7 files changed, 292 insertions(+), 12 deletions(-) diff --git a/js/ai/src/prompt.ts b/js/ai/src/prompt.ts index 4570fa57c..2e47d534d 100644 --- a/js/ai/src/prompt.ts +++ b/js/ai/src/prompt.ts @@ -27,6 +27,7 @@ import { GenerateRequestSchema, GenerateResponseChunkSchema, ModelArgument, + ModelMiddleware, } from './model.js'; import { ToolAction } from './tool.js'; @@ -51,6 +52,7 @@ export type PromptAction = Action< type: 'prompt'; }; }; + __config: PromptConfig; }; /** @@ -62,6 +64,7 @@ export interface PromptConfig { inputSchema?: I; inputJsonSchema?: JSONSchema7; metadata?: Record; + use?: ModelMiddleware[]; } /** @@ -147,12 +150,16 @@ export function definePrompt( const a = defineAction( registry, { - ...config, + name: config.name, + inputJsonSchema: config.inputJsonSchema, + inputSchema: config.inputSchema, + description: config.description, actionType: 'prompt', metadata: { ...(config.metadata || { prompt: {} }), type: 'prompt' }, }, fn ); + (a as PromptAction).__config = config; return a as PromptAction; } diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index 1601e7539..d212afdc9 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -131,6 +131,7 @@ import { loadPromptFolder, prompt, toFrontmatter, + type DotpromptAction, } from '@genkit-ai/dotprompt'; import { v4 as uuidv4 } from 'uuid'; import { BaseEvalDataPointSchema } from './evaluator.js'; @@ -463,13 +464,13 @@ export class Genkit implements HasRegistry { ); return this.wrapPromptActionInExecutablePrompt( dotprompt.promptAction! as PromptAction, - options, - dotprompt + options ); } else { const p = definePrompt( this.registry, { + ...options, name: options.name!, description: options.description, inputJsonSchema: options.input?.jsonSchema, @@ -513,8 +514,7 @@ export class Genkit implements HasRegistry { promptAction: PromptAction | Promise>, options: | Partial> - | Promise>>, - dotprompt?: Dotprompt> + | Promise>> ): ExecutablePrompt { const executablePrompt = async ( input?: z.infer, @@ -558,6 +558,9 @@ export class Genkit implements HasRegistry { const p = await promptAction; // If it's a dotprompt template, we invoke dotprompt template directly // because it can take in more PromptGenerateOptions (not just inputs). + const dotprompt: Dotprompt> | undefined = ( + p as DotpromptAction> + ).__dotprompt; const promptResult = await (dotprompt ? dotprompt.render(opt) : p(opt.input)); @@ -584,6 +587,13 @@ export class Genkit implements HasRegistry { }, model, } as GenerateOptions; + if ((promptResult as GenerateOptions).use) { + resultOptions.use = (promptResult as GenerateOptions).use; + } else if (p.__config?.use) { + resultOptions.use = p.__config?.use; + } else if (opt.use) { + resultOptions.use = opt.use; + } delete (resultOptions as any).input; if ((promptResult as GenerateOptions).prompt) { resultOptions.prompt = (promptResult as GenerateOptions).prompt; diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index 8a2b1e2c5..bc6a2a240 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { modelRef } from '@genkit-ai/ai/model'; +import { ModelMiddleware, modelRef } from '@genkit-ai/ai/model'; import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; import { Genkit, genkit } from '../src/genkit'; @@ -26,6 +26,168 @@ import { defineStaticResponseModel, } from './helpers'; +const wrapRequest: ModelMiddleware = async (req, next) => { + return next({ + ...req, + messages: [ + { + role: 'user', + content: [ + { + text: + '(' + + req.messages + .map((m) => m.content.map((c) => c.text).join()) + .join() + + ')', + }, + ], + }, + ], + }); +}; +const wrapResponse: ModelMiddleware = async (req, next) => { + const res = await next(req); + return { + message: { + role: 'model', + content: [ + { + text: '[' + res.message!.content.map((c) => c.text).join() + ']', + }, + ], + }, + finishReason: res.finishReason, + }; +}; + +describe('definePrompt - functional', () => { + let ai: Genkit; + + beforeEach(() => { + ai = genkit({ + model: 'echoModel', + }); + defineEchoModel(ai); + }); + + it('should apply middleware to a prompt call', async () => { + const prompt = ai.definePrompt( + { + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), + }, + }, + async (input) => { + return { + messages: [ + { + role: 'user', + content: [{ text: `hi ${input.name}` }], + }, + ], + }; + } + ); + + const response = await prompt( + { name: 'Genkit' }, + { use: [wrapRequest, wrapResponse] } + ); + assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]'); + }); + + it.only('should apply middleware configured on a prompt', async () => { + const prompt = ai.definePrompt( + { + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), + }, + use: [wrapRequest, wrapResponse], + }, + async (input) => { + return { + messages: [ + { + role: 'user', + content: [{ text: `hi ${input.name}` }], + }, + ], + }; + } + ); + + const response = await prompt({ name: 'Genkit' }); + assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]'); + }); + + it.only('should apply middleware to a prompt call on a looked up prompt', async () => { + ai.definePrompt( + { + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), + }, + use: [wrapRequest, wrapResponse], + }, + async (input) => { + return { + messages: [ + { + role: 'user', + content: [{ text: `hi ${input.name}` }], + }, + ], + }; + } + ); + + const prompt = ai.prompt('hi'); + + const response = await prompt({ name: 'Genkit' }); + assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]'); + }); + + it('should apply middleware configured on a prompt on a looked up prompt', async () => { + ai.definePrompt( + { + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), + }, + }, + async (input) => { + return { + messages: [ + { + role: 'user', + content: [{ text: `hi ${input.name}` }], + }, + ], + }; + } + ); + + const prompt = ai.prompt('hi'); + + const response = await prompt( + { name: 'Genkit' }, + { use: [wrapRequest, wrapResponse] } + ); + assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]'); + }); +}); + describe('definePrompt - dotprompt', () => { describe('default model', () => { let ai: Genkit; @@ -95,6 +257,86 @@ describe('definePrompt - dotprompt', () => { const response = await hi({ name: 'Genkit' }); assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); }); + + it('should apply middleware to a prompt call', async () => { + const prompt = ai.definePrompt( + { + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), + }, + }, + 'hi {{ name }}' + ); + + const response = await prompt( + { name: 'Genkit' }, + { use: [wrapRequest, wrapResponse] } + ); + assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]'); + }); + + it('should apply middleware configured on a prompt', async () => { + const prompt = ai.definePrompt( + { + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), + }, + use: [wrapRequest, wrapResponse], + }, + 'hi {{ name }}' + ); + + const response = await prompt({ name: 'Genkit' }); + assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]'); + }); + + it.only('should apply middleware to a prompt call on a looked up prompt', async () => { + ai.definePrompt( + { + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), + }, + use: [wrapRequest, wrapResponse], + }, + 'hi {{ name }}' + ); + + const prompt = ai.prompt('hi'); + + const response = await prompt({ name: 'Genkit' }); + assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]'); + }); + + it.only('should apply middleware configured on a prompt on a looked up prompt', async () => { + ai.definePrompt( + { + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), + }, + }, + 'hi {{ name }}' + ); + + const prompt = ai.prompt('hi'); + + const response = await prompt( + { name: 'Genkit' }, + { use: [wrapRequest, wrapResponse] } + ); + assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]'); + }); }); describe('default model ref', () => { diff --git a/js/plugins/dotprompt/src/index.ts b/js/plugins/dotprompt/src/index.ts index d68d20c64..ee07ec62a 100644 --- a/js/plugins/dotprompt/src/index.ts +++ b/js/plugins/dotprompt/src/index.ts @@ -19,9 +19,10 @@ import { readFileSync } from 'fs'; import { basename } from 'path'; import { toFrontmatter } from './metadata.js'; import { - defineDotprompt, Dotprompt, DotpromptRef, + defineDotprompt, + type DotpromptAction, type PromptGenerateOptions, } from './prompt.js'; import { loadPromptFolder, lookupPrompt } from './registry.js'; @@ -29,10 +30,11 @@ import { loadPromptFolder, lookupPrompt } from './registry.js'; export { type PromptMetadata } from './metadata.js'; export { defineHelper, definePartial } from './template.js'; export { - defineDotprompt, Dotprompt, + defineDotprompt, loadPromptFolder, toFrontmatter, + type DotpromptAction, type PromptGenerateOptions, }; diff --git a/js/plugins/dotprompt/src/metadata.ts b/js/plugins/dotprompt/src/metadata.ts index 3917fda9b..686d0b211 100644 --- a/js/plugins/dotprompt/src/metadata.ts +++ b/js/plugins/dotprompt/src/metadata.ts @@ -22,6 +22,7 @@ import { GenerationCommonConfigSchema, ModelArgument, + ModelMiddleware, } from '@genkit-ai/ai/model'; import { ToolArgument } from '@genkit-ai/ai/tool'; import { z } from '@genkit-ai/core'; @@ -79,6 +80,9 @@ export interface PromptMetadata< /** Arbitrary metadata to be used by code, tools, and libraries. */ metadata?: Record; + + /** Middleware to be used with this model call. */ + use?: ModelMiddleware[]; } /** diff --git a/js/plugins/dotprompt/src/prompt.ts b/js/plugins/dotprompt/src/prompt.ts index 52dbdfcff..83bbeec5c 100644 --- a/js/plugins/dotprompt/src/prompt.ts +++ b/js/plugins/dotprompt/src/prompt.ts @@ -48,6 +48,10 @@ import { compile } from './template.js'; export type PromptData = PromptFrontmatter & { template: string }; +export type DotpromptAction = PromptAction & { + __dotprompt: Dotprompt; +}; + export type PromptGenerateOptions< V = unknown, CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, @@ -78,6 +82,7 @@ export class Dotprompt implements PromptMetadata { output?: PromptMetadata['output']; tools?: PromptMetadata['tools']; config?: PromptMetadata['config']; + use?: PromptMetadata['use']; private _promptAction?: PromptAction; @@ -143,6 +148,7 @@ export class Dotprompt implements PromptMetadata { this.output = options.output; this.tools = options.tools; this.config = options.config; + this.use = options.use; this.template = template; this.hash = createHash('sha256').update(JSON.stringify(this)).digest('hex'); @@ -216,6 +222,7 @@ export class Dotprompt implements PromptMetadata { async (input?: I) => toGenerateRequest(this.registry, this.render({ input })) ); + (this._promptAction as DotpromptAction).__dotprompt = this; } get promptAction(): PromptAction | undefined { @@ -239,7 +246,7 @@ export class Dotprompt implements PromptMetadata { renderedPrompt = undefined; renderedMessages = messages; } - return { + const res = { model: options.model || this.model!, config: { ...this.config, ...options.config }, messages: renderedMessages, @@ -254,8 +261,12 @@ export class Dotprompt implements PromptMetadata { onChunk: options.onChunk ?? options.streamingCallback, returnToolRequests: options.returnToolRequests, maxTurns: options.maxTurns, - use: options.use, } as GenerateOptions; + const middleware = (options.use || []).concat(this.use || []); + if (middleware.length > 0) { + res.use = middleware; + } + return res; } /** diff --git a/js/plugins/dotprompt/tests/prompt_test.ts b/js/plugins/dotprompt/tests/prompt_test.ts index 19577dc2c..52da58f69 100644 --- a/js/plugins/dotprompt/tests/prompt_test.ts +++ b/js/plugins/dotprompt/tests/prompt_test.ts @@ -128,7 +128,11 @@ describe('Prompt', () => { ); const streamingCallback = (c) => console.log(c); - const middleware = []; + const middleware = [ + async (req, next) => { + return next(); + }, + ]; const rendered = prompt.render({ input: { name: 'Michael' }, @@ -140,7 +144,7 @@ describe('Prompt', () => { assert.strictEqual(rendered.onChunk, streamingCallback); assert.strictEqual(rendered.returnToolRequests, true); assert.strictEqual(rendered.maxTurns, 17); - assert.strictEqual(rendered.use, middleware); + assert.deepStrictEqual(rendered.use, middleware); }); it('should support system prompt with history', () => { From 9aad2986e6d679aab2c847728aa3ca6b4656917c Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 19 Dec 2024 15:50:35 -0500 Subject: [PATCH 145/562] chore: fix visibility of chat and session in typedoc (#1558) --- js/ai/src/chat.ts | 4 ++-- js/ai/src/session.ts | 2 +- js/genkit/src/index.ts | 7 ++++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/js/ai/src/chat.ts b/js/ai/src/chat.ts index f22b351ba..e84131057 100644 --- a/js/ai/src/chat.ts +++ b/js/ai/src/chat.ts @@ -33,7 +33,7 @@ import { Session, SessionStore, runWithSession, -} from './session'; +} from './session.js'; export const MAIN_THREAD = 'main'; @@ -242,7 +242,7 @@ export class Chat { return this._messages ?? []; } - async updateMessages(messages: MessageData[]): Promise { + private async updateMessages(messages: MessageData[]): Promise { this._messages = messages; await this.session.updateMessages(this.threadName, messages); } diff --git a/js/ai/src/session.ts b/js/ai/src/session.ts index 2d5af2fd9..2e7f0cf6d 100644 --- a/js/ai/src/session.ts +++ b/js/ai/src/session.ts @@ -17,7 +17,7 @@ import { z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; import { v4 as uuidv4 } from 'uuid'; -import { Chat, ChatOptions, MAIN_THREAD, PromptRenderOptions } from './chat'; +import { Chat, ChatOptions, MAIN_THREAD, PromptRenderOptions } from './chat.js'; import { ExecutablePrompt, GenerateOptions, diff --git a/js/genkit/src/index.ts b/js/genkit/src/index.ts index 723dee1fb..62f27c242 100644 --- a/js/genkit/src/index.ts +++ b/js/genkit/src/index.ts @@ -104,7 +104,12 @@ export { type ToolRequestPart, type ToolResponsePart, } from '@genkit-ai/ai'; -export { type SessionData, type SessionStore } from '@genkit-ai/ai/session'; +export { Chat } from '@genkit-ai/ai/chat'; +export { + Session, + type SessionData, + type SessionStore, +} from '@genkit-ai/ai/session'; export { FlowServer, GENKIT_CLIENT_HEADER, From d8c08c3c66960ce9dbc6c610511bfa54823fb535 Mon Sep 17 00:00:00 2001 From: Anthony Barone Date: Thu, 19 Dec 2024 17:28:34 -0500 Subject: [PATCH 146/562] chore: update to pnpm v9.15.0 (#1559) --- genkit-tools/package.json | 2 +- js/package.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/genkit-tools/package.json b/genkit-tools/package.json index a29c7b584..00c9dd422 100644 --- a/genkit-tools/package.json +++ b/genkit-tools/package.json @@ -25,5 +25,5 @@ "cross-spawn": "^7.0.5" } }, - "packageManager": "pnpm@9.13.2+sha256.ccce81bf7498c5f0f80e31749c1f8f03baba99d168f64590fc7e13fad3ea1938" + "packageManager": "pnpm@9.15.0+sha256.09a8fe31a34fda706354680619f4002f4ccef6dadff93240d24ef6c831f0fd28" } diff --git a/js/package.json b/js/package.json index 7925c4d62..bdc26e633 100644 --- a/js/package.json +++ b/js/package.json @@ -35,5 +35,5 @@ "cross-spawn": "^7.0.5" } }, - "packageManager": "pnpm@9.13.2+sha256.ccce81bf7498c5f0f80e31749c1f8f03baba99d168f64590fc7e13fad3ea1938" + "packageManager": "pnpm@9.15.0+sha256.09a8fe31a34fda706354680619f4002f4ccef6dadff93240d24ef6c831f0fd28" } diff --git a/package.json b/package.json index 89814835a..2d1d08989 100644 --- a/package.json +++ b/package.json @@ -44,5 +44,5 @@ "cross-spawn": "^7.0.5" } }, - "packageManager": "pnpm@9.13.2+sha256.ccce81bf7498c5f0f80e31749c1f8f03baba99d168f64590fc7e13fad3ea1938" + "packageManager": "pnpm@9.15.0+sha256.09a8fe31a34fda706354680619f4002f4ccef6dadff93240d24ef6c831f0fd28" } From 8da36d82bb1dc582bab01f47fa70e4cd953c4b57 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 01:58:18 +0000 Subject: [PATCH 147/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.2 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index acb8bd8bb..7efe9e808 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 8e712db4b3ab178a44f84190da3a460c91298815 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 01:58:21 +0000 Subject: [PATCH 148/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.2 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 8fb86a044..55571ffd8 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From b8345246672eafdb90ccaf8d53af09f89477293a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 01:58:23 +0000 Subject: [PATCH 149/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.2 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index e34229fba..e95432d8d 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From 36453994929cc35b909478f599a7ec1bfbf10916 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 01:59:33 +0000 Subject: [PATCH 150/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.2 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 090e39d06..f0bfe1234 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From 92c5d61058bae457c4785d16c7ac16f805195cfb Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 01:59:37 +0000 Subject: [PATCH 151/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.2 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index 0e4b82b74..ec2df3685 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From 62db6bd026f506214efd5c5b0ac510e4d2d9ed8c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 01:59:40 +0000 Subject: [PATCH 152/562] chore: bump genkit version to genkit@1.0.0-rc.2 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index 53c1fb7aa..d537ad3f7 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From 263693377bca6485cd030ea1c72a91589869f61a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 01:59:42 +0000 Subject: [PATCH 153/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.2 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index 0ac50dd20..62e5e9e5e 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From 374e097db6f3c7c7253bd6affe0bea5d82313eed Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 01:59:45 +0000 Subject: [PATCH 154/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.2 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 91e7a6e1b..82d218d7f 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From 5d39bcacc13eff8c47d32d5817ee7fbd844e271b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 01:59:48 +0000 Subject: [PATCH 155/562] chore: bump @genkit-ai/dotprompt version to @genkit-ai/dotprompt@1.0.0-rc.2 --- js/plugins/dotprompt/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dotprompt/package.json b/js/plugins/dotprompt/package.json index 3d4349e52..9bb65b9c5 100644 --- a/js/plugins/dotprompt/package.json +++ b/js/plugins/dotprompt/package.json @@ -9,7 +9,7 @@ "prompting", "templating" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From 301069dc133243b0cc6965408125112ae8e458f7 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 01:59:50 +0000 Subject: [PATCH 156/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.2 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 89b7af74b..d3a54deeb 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From aa5b8d69e24f06157e567dbff401377690058775 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 01:59:53 +0000 Subject: [PATCH 157/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.2 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 60780938f..b3014a4ba 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From ebce772587ba9844b1cc423176a25b5fadcfe732 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 01:59:56 +0000 Subject: [PATCH 158/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.2 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index 35f4b5862..ee1368e74 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From 6dab7c32b22048f0ca245b630ab9215b87a19604 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 01:59:59 +0000 Subject: [PATCH 159/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.2 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 5f30fd536..aa0704755 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From f4e3744379e0fba18a0ba9325ad212431e78249c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 02:00:02 +0000 Subject: [PATCH 160/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.2 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index ad26dc688..4056dae9e 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From 3a4d230a667219d1039f471ec02cac374bbf00d4 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 02:00:05 +0000 Subject: [PATCH 161/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.2 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index 6a36f0202..a7747a986 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From 806617b24c32ba6b315f02a96d29fc0e3f5a16e0 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 02:00:09 +0000 Subject: [PATCH 162/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.2 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index e21290fca..960053557 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From de66beb2916e2709b71f4b4ea6a26b0586ddb561 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 02:00:12 +0000 Subject: [PATCH 163/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.2 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 952340062..051caa953 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From 60cfc32cc01120c3b5c43d3a80c7861d712490fa Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 02:00:15 +0000 Subject: [PATCH 164/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.2 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index d8245d403..ea4dfa095 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From 69105afb65b39f31cd12b7110a7b8568eb51800b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 02:00:18 +0000 Subject: [PATCH 165/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.2 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index 0d61ef4a0..73f45cf44 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From f60d7aafc917cc9d69ec1f9457f1dbbe5bb76523 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 20 Dec 2024 02:00:21 +0000 Subject: [PATCH 166/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.2 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index 73d077b95..1de3220ff 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "type": "commonjs", "scripts": { "check": "tsc", From e52a0d72663b625e14e218cc0c0503c8a164f91e Mon Sep 17 00:00:00 2001 From: Anthony Barone Date: Fri, 20 Dec 2024 09:36:08 -0500 Subject: [PATCH 167/562] chore: update to pnpm v9.15.1 (#1561) --- genkit-tools/package.json | 2 +- js/package.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/genkit-tools/package.json b/genkit-tools/package.json index 00c9dd422..10dfc1367 100644 --- a/genkit-tools/package.json +++ b/genkit-tools/package.json @@ -25,5 +25,5 @@ "cross-spawn": "^7.0.5" } }, - "packageManager": "pnpm@9.15.0+sha256.09a8fe31a34fda706354680619f4002f4ccef6dadff93240d24ef6c831f0fd28" + "packageManager": "pnpm@9.15.1+sha256.9e534e70afef06374f6126b44bda5760947135ce16a30aef1010e965fb7e3e3e" } diff --git a/js/package.json b/js/package.json index bdc26e633..33222d047 100644 --- a/js/package.json +++ b/js/package.json @@ -35,5 +35,5 @@ "cross-spawn": "^7.0.5" } }, - "packageManager": "pnpm@9.15.0+sha256.09a8fe31a34fda706354680619f4002f4ccef6dadff93240d24ef6c831f0fd28" + "packageManager": "pnpm@9.15.1+sha256.9e534e70afef06374f6126b44bda5760947135ce16a30aef1010e965fb7e3e3e" } diff --git a/package.json b/package.json index 2d1d08989..7ec04e2ae 100644 --- a/package.json +++ b/package.json @@ -44,5 +44,5 @@ "cross-spawn": "^7.0.5" } }, - "packageManager": "pnpm@9.15.0+sha256.09a8fe31a34fda706354680619f4002f4ccef6dadff93240d24ef6c831f0fd28" + "packageManager": "pnpm@9.15.1+sha256.9e534e70afef06374f6126b44bda5760947135ce16a30aef1010e965fb7e3e3e" } From 9aad90e24fbd0dc27c9c173e79817f51b2bd62f8 Mon Sep 17 00:00:00 2001 From: Andrew Brook Date: Mon, 23 Dec 2024 09:18:52 -0500 Subject: [PATCH 168/562] fix missing return (#1562) --- js/ai/src/retriever.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/js/ai/src/retriever.ts b/js/ai/src/retriever.ts index 1a116055e..c67c9eee7 100644 --- a/js/ai/src/retriever.ts +++ b/js/ai/src/retriever.ts @@ -370,6 +370,7 @@ function itemToMetadata( if (Array.isArray(options.metadata) && typeof item === 'object') { const out: Record = {}; options.metadata.forEach((key) => (out[key] = item[key])); + return out; } if (typeof options.metadata === 'function') return options.metadata(item); if (!options.metadata && typeof item === 'object') { From 99eb34727a1daf7889c18d808de244686fd8fa9b Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:01:36 +0000 Subject: [PATCH 169/562] feat(evals): Support file based evals in "runNewEvaluation" #1579 --- genkit-tools/cli/src/commands/eval-flow.ts | 2 +- genkit-tools/common/src/eval/evaluate.ts | 52 +++++++++++++--------- genkit-tools/common/src/types/apis.ts | 5 ++- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/genkit-tools/cli/src/commands/eval-flow.ts b/genkit-tools/cli/src/commands/eval-flow.ts index f3f58765b..8225b58a8 100644 --- a/genkit-tools/cli/src/commands/eval-flow.ts +++ b/genkit-tools/cli/src/commands/eval-flow.ts @@ -130,7 +130,7 @@ export const evalFlow = new Command('eval:flow') const evalDataset = await runInference({ manager, actionRef, - evalFlowInput, + evalInferenceInput: evalFlowInput, auth: options.auth, }); diff --git a/genkit-tools/common/src/eval/evaluate.ts b/genkit-tools/common/src/eval/evaluate.ts index 5cdaa0faa..1055a4636 100644 --- a/genkit-tools/common/src/eval/evaluate.ts +++ b/genkit-tools/common/src/eval/evaluate.ts @@ -66,25 +66,38 @@ export async function runNewEvaluation( manager: RuntimeManager, request: RunNewEvaluationRequest ): Promise { - const { datasetId, actionRef, evaluators } = request; - const datasetStore = await getDatasetStore(); - logger.info(`Fetching dataset ${datasetId}...`); - const dataset = await datasetStore.getDataset(datasetId); - const datasetMetadatas = await datasetStore.listDatasets(); - const targetDatasetMetadata = datasetMetadatas.find( - (d) => d.datasetId === datasetId - ); - const datasetVersion = targetDatasetMetadata?.version; + const { dataSource, actionRef, evaluators } = request; + const { datasetId, data } = dataSource; + if (!datasetId && !data) { + throw new Error(`Either 'data' or 'datasetId' must be provided`); + } + + let evalInferenceInput: EvalInferenceInput; + let metadata = {}; + if (datasetId) { + const datasetStore = await getDatasetStore(); + logger.info(`Fetching dataset ${datasetId}...`); + const dataset = await datasetStore.getDataset(datasetId); + if (dataset.length === 0) { + throw new Error(`Dataset ${datasetId} is empty`); + } + evalInferenceInput = EvalInferenceInputSchema.parse(dataset); - if (dataset.length === 0) { - throw new Error(`Dataset ${datasetId} is empty`); + const datasetMetadatas = await datasetStore.listDatasets(); + const targetDatasetMetadata = datasetMetadatas.find( + (d) => d.datasetId === datasetId + ); + const datasetVersion = targetDatasetMetadata?.version; + metadata = { datasetId, datasetVersion }; + } else { + evalInferenceInput = data!; } logger.info('Running inference...'); const evalDataset = await runInference({ manager, actionRef, - evalFlowInput: EvalInferenceInputSchema.parse(dataset), + evalInferenceInput, auth: request.options?.auth, actionConfig: request.options?.actionConfig, }); @@ -98,9 +111,8 @@ export async function runNewEvaluation( evaluatorActions, evalDataset, augments: { + ...metadata, actionRef, - datasetId, - datasetVersion, actionConfig: request.options?.actionConfig, }, }); @@ -111,11 +123,11 @@ export async function runNewEvaluation( export async function runInference(params: { manager: RuntimeManager; actionRef: string; - evalFlowInput: EvalInferenceInput; + evalInferenceInput: EvalInferenceInput; auth?: string; actionConfig?: any; }): Promise { - const { manager, actionRef, evalFlowInput, auth, actionConfig } = params; + const { manager, actionRef, evalInferenceInput, auth, actionConfig } = params; if (!isSupportedActionRef(actionRef)) { throw new Error('Inference is only supported on flows and models'); } @@ -123,7 +135,7 @@ export async function runInference(params: { const evalDataset: EvalInput[] = await bulkRunAction({ manager, actionRef, - evalFlowInput, + evalInferenceInput, auth, actionConfig, }); @@ -210,13 +222,13 @@ export async function getMatchingEvaluatorActions( async function bulkRunAction(params: { manager: RuntimeManager; actionRef: string; - evalFlowInput: EvalInferenceInput; + evalInferenceInput: EvalInferenceInput; auth?: string; actionConfig?: any; }): Promise { - const { manager, actionRef, evalFlowInput, auth, actionConfig } = params; + const { manager, actionRef, evalInferenceInput, auth, actionConfig } = params; const isModelAction = actionRef.startsWith('/model'); - let testCases: TestCase[] = evalFlowInput.map((c) => ({ + let testCases: TestCase[] = evalInferenceInput.map((c) => ({ input: c.input, reference: c.reference, testCaseId: c.testCaseId ?? generateTestCaseId(), diff --git a/genkit-tools/common/src/types/apis.ts b/genkit-tools/common/src/types/apis.ts index bd300cc73..430296929 100644 --- a/genkit-tools/common/src/types/apis.ts +++ b/genkit-tools/common/src/types/apis.ts @@ -131,7 +131,10 @@ export const UpdateDatasetRequestSchema = z.object({ export type UpdateDatasetRequest = z.infer; export const RunNewEvaluationRequestSchema = z.object({ - datasetId: z.string(), + dataSource: z.object({ + datasetId: z.string().optional(), + data: EvalInferenceInputSchema.optional(), + }), actionRef: z.string(), evaluators: z.array(z.string()).optional(), options: z From 6ace63e9e4ebe49bdc2cf4e82c9a33430b08d15f Mon Sep 17 00:00:00 2001 From: Alexander Nohe Date: Tue, 7 Jan 2025 13:03:02 -0800 Subject: [PATCH 170/562] docs: Point to Genkit template for IDX (#1586) The IDX button was using the generic Github import link. This updates the URL to the Genkit template for IDX. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ce658b5c..f30ea7da3 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ Find excellent examples of community-built plugins for OpenAI, Anthropic, Cohere Want to skip the local setup? Click below to try out Genkit using [Project IDX](https://idx.dev), Google's AI-assisted workspace for full-stack app development in the cloud. - + Try in IDX Date: Tue, 7 Jan 2025 14:24:26 -0800 Subject: [PATCH 171/562] docs: Point samples to new locations (#1585) The samples in the readme were pointing to non-existent locations since the location was migrated to the quickstart-nodejs repo. Migrated the sample links to point to the right location. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f30ea7da3..84a4bef37 100644 --- a/README.md +++ b/README.md @@ -101,11 +101,11 @@ Want to skip the local setup? Click below to try out Genkit using [Project IDX]( Take a look at some samples of Genkit in use: -- ["AI barista"](samples/js-coffee-shop/) -- demonstrates simple LLM usage -- [A simple chatbot with a JavaScript frontend](samples/chatbot/) -- add history to LLM sessions -- [Restaurant menu Q&A app](samples/js-menu/) -- this sample shows progressively +- ["AI barista"](https://github.com/firebase/quickstart-nodejs/tree/master/genkit/js-coffee-shop) -- demonstrates simple LLM usage +- [A simple chatbot with a JavaScript frontend](https://github.com/firebase/quickstart-nodejs/tree/master/genkit/chatbot) -- add history to LLM sessions +- [Restaurant menu Q&A app](https://github.com/firebase/quickstart-nodejs/tree/master/genkit/js-menu) -- this sample shows progressively more sophisticated versions of a menu understanding app. -- [Streaming to an Angular frontend](samples/js-angular/) +- [Streaming to an Angular frontend](https://github.com/firebase/quickstart-nodejs/tree/master/genkit/js-angular) ## Connect with us From 3db0b2be47483d3a5aacfce283b4de8d1ff46727 Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Wed, 8 Jan 2025 11:01:49 -0500 Subject: [PATCH 172/562] feat: export GenerateRequest JSON schema for Dev UI (#1588) --- genkit-tools/common/src/types/eval.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/genkit-tools/common/src/types/eval.ts b/genkit-tools/common/src/types/eval.ts index eed656619..57782b42f 100644 --- a/genkit-tools/common/src/types/eval.ts +++ b/genkit-tools/common/src/types/eval.ts @@ -30,7 +30,7 @@ import { GenerateRequestSchema } from './model'; */ /** - * Supported datatype when running eval-inference using models + * Supported datatype for model datasets */ export const ModelInferenceInputSchema = z.union([ z.string(), @@ -45,6 +45,17 @@ export const ModelInferenceInputJSONSchema = zodToJsonSchema( } ) as JSONSchema7; +/** + * GenerateRequest JSON schema to support eval-inference using models + */ +export const GenerateRequestJSONSchema = zodToJsonSchema( + GenerateRequestSchema, + { + $refStrategy: 'none', + removeAdditionalStrategy: 'strict', + } +) as JSONSchema7; + /** * A single sample to be used for inference. **/ From 2845d32da67bf084faaa2d2bc16b9e10d0013e89 Mon Sep 17 00:00:00 2001 From: Tyler Freeman Date: Wed, 8 Jan 2025 10:54:43 -0800 Subject: [PATCH 173/562] docs(eval): fix evaluator plugin docs so code compiles (#1497) --- docs/plugin-authoring-evaluator.md | 58 +++++++++++++++--------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/docs/plugin-authoring-evaluator.md b/docs/plugin-authoring-evaluator.md index 0e7a9b9ce..d7ca8a7bd 100644 --- a/docs/plugin-authoring-evaluator.md +++ b/docs/plugin-authoring-evaluator.md @@ -201,6 +201,7 @@ Heuristic evaluators in Genkit are made up of 2 components: Just like the LLM-based evaluator, define the scoring function. In this case, the scoring function does not need to know about the judge LLM or its config. ```ts +import { EvalResponses } from 'genkit'; import { BaseEvalDataPoint, Score } from 'genkit/evaluator'; const US_PHONE_REGEX = @@ -218,8 +219,8 @@ export async function usPhoneRegexScore( } const matches = US_PHONE_REGEX.test(d.output as string); const reasoning = matches - ? `Output matched regex ${regex.source}` - : `Output did not match regex ${regex.source}`; + ? `Output matched regex ${US_PHONE_REGEX.source}` + : `Output did not match regex ${US_PHONE_REGEX.source}`; return { score: matches, details: { reasoning }, @@ -227,9 +228,9 @@ export async function usPhoneRegexScore( } /** - * Create an EvalResponse from an individual scored datapoint. + * Create an EvalResponses from an individual scored datapoint. */ -function fillScores(dataPoint: BaseEvalDataPoint, score: Score): EvalResponse { +function fillScores(dataPoint: BaseEvalDataPoint, score: Score): EvalResponses { return { testCaseId: dataPoint.testCaseId, evaluation: score, @@ -240,31 +241,31 @@ function fillScores(dataPoint: BaseEvalDataPoint, score: Score): EvalResponse { #### Define the evaluator action ```ts +import { Genkit } from 'genkit'; import { BaseEvalDataPoint, EvaluatorAction } from 'genkit/evaluator'; /** * Configures a regex evaluator to match a US phone number. */ export function createUSPhoneRegexEvaluator( - metrics: RegexMetric[] -): EvaluatorAction[] { - return metrics.map((metric) => { - const regexMetric = metric as RegexMetric; - return defineEvaluator( - { - name: `myAwesomeEval/${metric.name.toLocaleLowerCase()}`, - displayName: 'Regex Match', - definition: - 'Runs the output against a regex and responds with true if a match is found and false otherwise.', - isBilled: false, - }, - async (datapoint: BaseEvalDataPoint) => { - const score = await usPhoneRegexScore(datapoint); - return fillScores(datapoint, score); - } - ); - }); + ai: Genkit, + metric: MyAwesomeMetric +): EvaluatorAction { + return ai.defineEvaluator( + { + name: `myAwesomeEval/${metric.toLocaleLowerCase()}`, + displayName: 'Regex Match', + definition: + 'Runs the output against a regex and responds with true if a match is found and false otherwise.', + isBilled: false, + }, + async (datapoint: BaseEvalDataPoint) => { + const score = await usPhoneRegexScore(datapoint); + return fillScores(datapoint, score); + } + ); } + ``` ## Configuration @@ -278,6 +279,7 @@ At a minimum it will need to take the definition of which metrics to register. ```ts export enum MyAwesomeMetric { WORD_COUNT = 'WORD_COUNT', + DELICIOUSNESS = 'DELICIOUSNESS', US_PHONE_REGEX_MATCH = 'US_PHONE_REGEX_MATCH', } @@ -309,22 +311,22 @@ export function myAwesomeEval( options: PluginOptions ): GenkitPlugin { // Define the new plugin - return genkitPlugin( + return genkitPlugin( 'myAwesomeEval', async (ai: Genkit) => { const { judge, judgeConfig, metrics } = options; const evaluators: EvaluatorAction[] = metrics.map((metric) => { switch (metric) { - case DELICIOUSNESS: + case MyAwesomeMetric.DELICIOUSNESS: // This evaluator requires an LLM as judge return createDeliciousnessEvaluator(ai, judge, judgeConfig); - case US_PHONE_REGEX_MATCH: + case MyAwesomeMetric.US_PHONE_REGEX_MATCH: // This evaluator does not require an LLM - return createUSPhoneRegexEvaluator(); + return createUSPhoneRegexEvaluator(ai, metric); } }); - return { evaluators }; - }); + } + ); } export default myAwesomeEval; ``` From 38baf3009a4d07920bacdb7014fce1f3599cc98d Mon Sep 17 00:00:00 2001 From: Alex Pascal Date: Thu, 9 Jan 2025 07:58:57 -0600 Subject: [PATCH 174/562] breaking(go): Refactored into instance-based `Genkit` and `Registry`. (#1459) --- go/ai/embedder.go | 17 +- go/ai/generate.go | 55 +++-- go/ai/generator_test.go | 18 +- go/ai/prompt.go | 13 +- go/ai/retriever.go | 25 +- go/ai/tools.go | 8 +- go/core/action.go | 34 +-- go/core/action_test.go | 26 +- go/core/core.go | 4 +- go/genkit/flow.go | 9 +- go/genkit/flow_test.go | 17 +- go/genkit/genkit.go | 225 +++++++++++++++++- go/genkit/genkit_test.go | 6 +- go/genkit/servers.go | 12 +- go/genkit/servers_test.go | 12 +- go/internal/doc-snippets/dotprompt.go | 55 ++++- go/internal/doc-snippets/flows.go | 45 +++- go/internal/doc-snippets/gcp.go | 7 + go/internal/doc-snippets/googleai.go | 17 +- go/internal/doc-snippets/init/main.go | 16 +- .../doc-snippets/modelplugin/modelplugin.go | 8 +- go/internal/doc-snippets/models.go | 65 +++-- go/internal/doc-snippets/ollama.go | 12 +- go/internal/doc-snippets/pinecone.go | 19 +- go/internal/doc-snippets/prompts.go | 35 ++- go/internal/doc-snippets/rag/main.go | 40 +++- .../telemetryplugin/telemetryplugin.go | 11 +- go/internal/doc-snippets/vertexai.go | 21 +- go/internal/fakeembedder/fakeembedder_test.go | 8 +- go/internal/registry/registry.go | 14 -- go/plugins/dotprompt/dotprompt.go | 41 ++-- go/plugins/dotprompt/dotprompt_test.go | 33 ++- go/plugins/dotprompt/genkit.go | 19 +- go/plugins/dotprompt/genkit_test.go | 36 ++- go/plugins/dotprompt/render_test.go | 20 +- go/plugins/googleai/googleai.go | 39 +-- go/plugins/googleai/googleai_test.go | 32 ++- go/plugins/googlecloud/googlecloud.go | 6 +- go/plugins/localvec/localvec.go | 23 +- go/plugins/localvec/localvec_test.go | 23 +- go/plugins/ollama/embed.go | 13 +- go/plugins/ollama/ollama.go | 15 +- go/plugins/ollama/ollama_live_test.go | 14 +- go/plugins/pinecone/genkit.go | 25 +- go/plugins/pinecone/genkit_test.go | 12 +- go/plugins/vertexai/vertexai.go | 39 +-- go/plugins/vertexai/vertexai_test.go | 22 +- go/plugins/weaviate/weaviate.go | 15 +- go/plugins/weaviate/weaviate_test.go | 10 +- go/samples/coffee-shop/main.go | 39 +-- go/samples/firebase-auth/main.go | 13 +- go/samples/flow-sample1/main.go | 19 +- go/samples/menu/main.go | 26 +- go/samples/menu/s01.go | 13 +- go/samples/menu/s02.go | 10 +- go/samples/menu/s03.go | 10 +- go/samples/menu/s04.go | 10 +- go/samples/menu/s05.go | 17 +- go/samples/pgvector/main.go | 28 ++- go/samples/rag/main.go | 20 +- go/tests/test_app/main.go | 12 +- js/plugins/dotprompt/src/index.ts | 1 - 62 files changed, 1045 insertions(+), 464 deletions(-) diff --git a/go/ai/embedder.go b/go/ai/embedder.go index 971810fce..da3dae507 100644 --- a/go/ai/embedder.go +++ b/go/ai/embedder.go @@ -20,6 +20,7 @@ import ( "github.com/firebase/genkit/go/core" "github.com/firebase/genkit/go/internal/atype" + "github.com/firebase/genkit/go/internal/registry" ) // Embedder represents an embedder that can perform content embedding. @@ -56,19 +57,23 @@ type DocumentEmbedding struct { // DefineEmbedder registers the given embed function as an action, and returns an // [Embedder] that runs it. -func DefineEmbedder(provider, name string, embed func(context.Context, *EmbedRequest) (*EmbedResponse, error)) Embedder { - return (*embedderActionDef)(core.DefineAction(provider, name, atype.Embedder, nil, embed)) +func DefineEmbedder( + r *registry.Registry, + provider, name string, + embed func(context.Context, *EmbedRequest) (*EmbedResponse, error), +) Embedder { + return (*embedderActionDef)(core.DefineAction(r, provider, name, atype.Embedder, nil, embed)) } // IsDefinedEmbedder reports whether an embedder is defined. -func IsDefinedEmbedder(provider, name string) bool { - return LookupEmbedder(provider, name) != nil +func IsDefinedEmbedder(r *registry.Registry, provider, name string) bool { + return LookupEmbedder(r, provider, name) != nil } // LookupEmbedder looks up an [Embedder] registered by [DefineEmbedder]. // It returns nil if the embedder was not defined. -func LookupEmbedder(provider, name string) Embedder { - action := core.LookupActionFor[*EmbedRequest, *EmbedResponse, struct{}](atype.Embedder, provider, name) +func LookupEmbedder(r *registry.Registry, provider, name string) Embedder { + action := core.LookupActionFor[*EmbedRequest, *EmbedResponse, struct{}](r, atype.Embedder, provider, name) if action == nil { return nil } diff --git a/go/ai/generate.go b/go/ai/generate.go index 54b14679b..5a02b03ca 100644 --- a/go/ai/generate.go +++ b/go/ai/generate.go @@ -27,6 +27,7 @@ import ( "github.com/firebase/genkit/go/core/logger" "github.com/firebase/genkit/go/internal/atype" "github.com/firebase/genkit/go/internal/base" + "github.com/firebase/genkit/go/internal/registry" ) // Model represents a model that can perform content generation tasks. @@ -34,7 +35,7 @@ type Model interface { // Name returns the registry name of the model. Name() string // Generate applies the [Model] to provided request, handling tool requests and handles streaming. - Generate(ctx context.Context, req *ModelRequest, cb ModelStreamingCallback) (*ModelResponse, error) + Generate(ctx context.Context, r *registry.Registry, req *ModelRequest, cb ModelStreamingCallback) (*ModelResponse, error) } type modelActionDef core.Action[*ModelRequest, *ModelResponse, *ModelResponseChunk] @@ -60,7 +61,12 @@ type ModelMetadata struct { // DefineModel registers the given generate function as an action, and returns a // [Model] that runs it. -func DefineModel(provider, name string, metadata *ModelMetadata, generate func(context.Context, *ModelRequest, ModelStreamingCallback) (*ModelResponse, error)) Model { +func DefineModel( + r *registry.Registry, + provider, name string, + metadata *ModelMetadata, + generate func(context.Context, *ModelRequest, ModelStreamingCallback) (*ModelResponse, error), +) Model { metadataMap := map[string]any{} if metadata == nil { // Always make sure there's at least minimal metadata. @@ -79,20 +85,20 @@ func DefineModel(provider, name string, metadata *ModelMetadata, generate func(c } metadataMap["supports"] = supports - return (*modelActionDef)(core.DefineStreamingAction(provider, name, atype.Model, map[string]any{ + return (*modelActionDef)(core.DefineStreamingAction(r, provider, name, atype.Model, map[string]any{ "model": metadataMap, }, generate)) } // IsDefinedModel reports whether a model is defined. -func IsDefinedModel(provider, name string) bool { - return core.LookupActionFor[*ModelRequest, *ModelResponse, *ModelResponseChunk](atype.Model, provider, name) != nil +func IsDefinedModel(r *registry.Registry, provider, name string) bool { + return core.LookupActionFor[*ModelRequest, *ModelResponse, *ModelResponseChunk](r, atype.Model, provider, name) != nil } // LookupModel looks up a [Model] registered by [DefineModel]. // It returns nil if the model was not defined. -func LookupModel(provider, name string) Model { - action := core.LookupActionFor[*ModelRequest, *ModelResponse, *ModelResponseChunk](atype.Model, provider, name) +func LookupModel(r *registry.Registry, provider, name string) Model { + action := core.LookupActionFor[*ModelRequest, *ModelResponse, *ModelResponseChunk](r, atype.Model, provider, name) if action == nil { return nil } @@ -102,6 +108,7 @@ func LookupModel(provider, name string) Model { // generateParams represents various params of the Generate call. type generateParams struct { Request *ModelRequest + Model Model Stream ModelStreamingCallback History []*Message SystemPrompt *Message @@ -110,6 +117,14 @@ type generateParams struct { // GenerateOption configures params of the Generate call. type GenerateOption func(req *generateParams) error +// WithModel sets the model to use for the generate request. +func WithModel(m Model) GenerateOption { + return func(req *generateParams) error { + req.Model = m + return nil + } +} + // WithTextPrompt adds a simple text user prompt to ModelRequest. func WithTextPrompt(prompt string) GenerateOption { return func(req *generateParams) error { @@ -174,6 +189,9 @@ func WithContext(c ...any) GenerateOption { // WithTools adds provided tools to ModelRequest. func WithTools(tools ...Tool) GenerateOption { return func(req *generateParams) error { + if req.Request.Tools != nil { + return errors.New("cannot set Request.Tools (WithTools) more than once") + } var toolDefs []*ToolDefinition for _, t := range tools { toolDefs = append(toolDefs, t.Definition()) @@ -221,7 +239,7 @@ func WithStreaming(cb ModelStreamingCallback) GenerateOption { } // Generate run generate request for this model. Returns ModelResponse struct. -func Generate(ctx context.Context, m Model, opts ...GenerateOption) (*ModelResponse, error) { +func Generate(ctx context.Context, r *registry.Registry, opts ...GenerateOption) (*ModelResponse, error) { req := &generateParams{ Request: &ModelRequest{}, } @@ -231,6 +249,9 @@ func Generate(ctx context.Context, m Model, opts ...GenerateOption) (*ModelRespo return nil, err } } + if req.Model == nil { + return nil, errors.New("model is required") + } if req.History != nil { prev := req.Request.Messages req.Request.Messages = req.History @@ -242,12 +263,12 @@ func Generate(ctx context.Context, m Model, opts ...GenerateOption) (*ModelRespo req.Request.Messages = append(req.Request.Messages, prev...) } - return m.Generate(ctx, req.Request, req.Stream) + return req.Model.Generate(ctx, r, req.Request, req.Stream) } // GenerateText run generate request for this model. Returns generated text only. -func GenerateText(ctx context.Context, m Model, opts ...GenerateOption) (string, error) { - res, err := Generate(ctx, m, opts...) +func GenerateText(ctx context.Context, r *registry.Registry, opts ...GenerateOption) (string, error) { + res, err := Generate(ctx, r, opts...) if err != nil { return "", err } @@ -257,9 +278,9 @@ func GenerateText(ctx context.Context, m Model, opts ...GenerateOption) (string, // Generate run generate request for this model. Returns ModelResponse struct. // TODO: Stream GenerateData with partial JSON -func GenerateData(ctx context.Context, m Model, value any, opts ...GenerateOption) (*ModelResponse, error) { +func GenerateData(ctx context.Context, r *registry.Registry, value any, opts ...GenerateOption) (*ModelResponse, error) { opts = append(opts, WithOutputSchema(value)) - resp, err := Generate(ctx, m, opts...) + resp, err := Generate(ctx, r, opts...) if err != nil { return nil, err } @@ -271,7 +292,7 @@ func GenerateData(ctx context.Context, m Model, value any, opts ...GenerateOptio } // Generate applies the [Action] to provided request, handling tool requests and handles streaming. -func (m *modelActionDef) Generate(ctx context.Context, req *ModelRequest, cb ModelStreamingCallback) (*ModelResponse, error) { +func (m *modelActionDef) Generate(ctx context.Context, r *registry.Registry, req *ModelRequest, cb ModelStreamingCallback) (*ModelResponse, error) { if m == nil { return nil, errors.New("Generate called on a nil Model; check that all models are defined") } @@ -292,7 +313,7 @@ func (m *modelActionDef) Generate(ctx context.Context, req *ModelRequest, cb Mod } resp.Message = msg - newReq, err := handleToolRequest(ctx, req, resp) + newReq, err := handleToolRequest(ctx, r, req, resp) if err != nil { return nil, err } @@ -362,7 +383,7 @@ func validMessage(m *Message, output *ModelRequestOutput) (*Message, error) { // handleToolRequest checks if a tool was requested by a model. // If a tool was requested, this runs the tool and returns an // updated ModelRequest. If no tool was requested this returns nil. -func handleToolRequest(ctx context.Context, req *ModelRequest, resp *ModelResponse) (*ModelRequest, error) { +func handleToolRequest(ctx context.Context, r *registry.Registry, req *ModelRequest, resp *ModelResponse) (*ModelRequest, error) { msg := resp.Message if msg == nil || len(msg.Content) == 0 { return nil, nil @@ -373,7 +394,7 @@ func handleToolRequest(ctx context.Context, req *ModelRequest, resp *ModelRespon } toolReq := part.ToolRequest - tool := LookupTool(toolReq.Name) + tool := LookupTool(r, toolReq.Name) if tool == nil { return nil, fmt.Errorf("tool %v not found", toolReq.Name) } diff --git a/go/ai/generator_test.go b/go/ai/generator_test.go index fdd9a7d2f..0c5796b92 100644 --- a/go/ai/generator_test.go +++ b/go/ai/generator_test.go @@ -20,6 +20,7 @@ import ( "strings" "testing" + "github.com/firebase/genkit/go/internal/registry" test_utils "github.com/firebase/genkit/go/tests/utils" "github.com/google/go-cmp/cmp" ) @@ -30,7 +31,9 @@ type GameCharacter struct { Backstory string } -var echoModel = DefineModel("test", "echo", nil, func(ctx context.Context, gr *ModelRequest, msc ModelStreamingCallback) (*ModelResponse, error) { +var r, _ = registry.New() + +var echoModel = DefineModel(r, "test", "echo", nil, func(ctx context.Context, gr *ModelRequest, msc ModelStreamingCallback) (*ModelResponse, error) { if msc != nil { msc(ctx, &ModelResponseChunk{ Content: []*Part{NewTextPart("stream!")}, @@ -49,7 +52,7 @@ var echoModel = DefineModel("test", "echo", nil, func(ctx context.Context, gr *M }) // with tools -var gablorkenTool = DefineTool("gablorken", "use when need to calculate a gablorken", +var gablorkenTool = DefineTool(r, "gablorken", "use when need to calculate a gablorken", func(ctx context.Context, input struct { Value float64 Over float64 @@ -292,7 +295,8 @@ func TestGenerate(t *testing.T) { wantStreamText := "stream!" streamText := "" - res, err := Generate(context.Background(), echoModel, + res, err := Generate(context.Background(), r, + WithModel(echoModel), WithTextPrompt(charJSONmd), WithMessages(NewModelTextMessage("banana again")), WithSystemPrompt("you are"), @@ -328,12 +332,12 @@ func TestGenerate(t *testing.T) { func TestIsDefinedModel(t *testing.T) { t.Run("should return true", func(t *testing.T) { - if IsDefinedModel("test", "echo") != true { + if IsDefinedModel(r, "test", "echo") != true { t.Errorf("IsDefinedModel did not return true") } }) t.Run("should return false", func(t *testing.T) { - if IsDefinedModel("foo", "bar") != false { + if IsDefinedModel(r, "foo", "bar") != false { t.Errorf("IsDefinedModel did not return false") } }) @@ -341,12 +345,12 @@ func TestIsDefinedModel(t *testing.T) { func TestLookupModel(t *testing.T) { t.Run("should return model", func(t *testing.T) { - if LookupModel("test", "echo") == nil { + if LookupModel(r, "test", "echo") == nil { t.Errorf("LookupModel did not return model") } }) t.Run("should return nil", func(t *testing.T) { - if LookupModel("foo", "bar") != nil { + if LookupModel(r, "foo", "bar") != nil { t.Errorf("LookupModel did not return nil") } }) diff --git a/go/ai/prompt.go b/go/ai/prompt.go index a6e5e73ae..7643997e3 100644 --- a/go/ai/prompt.go +++ b/go/ai/prompt.go @@ -21,6 +21,7 @@ import ( "github.com/firebase/genkit/go/core" "github.com/firebase/genkit/go/internal/atype" + "github.com/firebase/genkit/go/internal/registry" "github.com/invopop/jsonschema" ) @@ -33,24 +34,24 @@ type Prompt core.Action[any, *ModelRequest, struct{}] // The prompt expects some input described by inputSchema. // DefinePrompt registers the function as an action, // and returns a [Prompt] that runs it. -func DefinePrompt(provider, name string, metadata map[string]any, inputSchema *jsonschema.Schema, render func(context.Context, any) (*ModelRequest, error)) *Prompt { +func DefinePrompt(r *registry.Registry, provider, name string, metadata map[string]any, inputSchema *jsonschema.Schema, render func(context.Context, any) (*ModelRequest, error)) *Prompt { mm := maps.Clone(metadata) if mm == nil { mm = make(map[string]any) } mm["type"] = "prompt" - return (*Prompt)(core.DefineActionWithInputSchema(provider, name, atype.Prompt, mm, inputSchema, render)) + return (*Prompt)(core.DefineActionWithInputSchema(r, provider, name, atype.Prompt, mm, inputSchema, render)) } // IsDefinedPrompt reports whether a [Prompt] is defined. -func IsDefinedPrompt(provider, name string) bool { - return LookupPrompt(provider, name) != nil +func IsDefinedPrompt(r *registry.Registry, provider, name string) bool { + return LookupPrompt(r, provider, name) != nil } // LookupPrompt looks up a [Prompt] registered by [DefinePrompt]. // It returns nil if the prompt was not defined. -func LookupPrompt(provider, name string) *Prompt { - return (*Prompt)(core.LookupActionFor[any, *ModelRequest, struct{}](atype.Prompt, provider, name)) +func LookupPrompt(r *registry.Registry, provider, name string) *Prompt { + return (*Prompt)(core.LookupActionFor[any, *ModelRequest, struct{}](r, atype.Prompt, provider, name)) } // Render renders the [Prompt] with some input data. diff --git a/go/ai/retriever.go b/go/ai/retriever.go index 7f5b0c63b..a239eb755 100644 --- a/go/ai/retriever.go +++ b/go/ai/retriever.go @@ -20,6 +20,7 @@ import ( "github.com/firebase/genkit/go/core" "github.com/firebase/genkit/go/internal/atype" + "github.com/firebase/genkit/go/internal/registry" ) // Retriever represents a document retriever. @@ -67,39 +68,39 @@ type RetrieverResponse struct { // DefineIndexer registers the given index function as an action, and returns an // [Indexer] that runs it. -func DefineIndexer(provider, name string, index func(context.Context, *IndexerRequest) error) Indexer { +func DefineIndexer(r *registry.Registry, provider, name string, index func(context.Context, *IndexerRequest) error) Indexer { f := func(ctx context.Context, req *IndexerRequest) (struct{}, error) { return struct{}{}, index(ctx, req) } - return (*indexerActionDef)(core.DefineAction(provider, name, atype.Indexer, nil, f)) + return (*indexerActionDef)(core.DefineAction(r, provider, name, atype.Indexer, nil, f)) } // IsDefinedIndexer reports whether an [Indexer] is defined. -func IsDefinedIndexer(provider, name string) bool { - return (*indexerActionDef)(core.LookupActionFor[*IndexerRequest, struct{}, struct{}](atype.Indexer, provider, name)) != nil +func IsDefinedIndexer(r *registry.Registry, provider, name string) bool { + return (*indexerActionDef)(core.LookupActionFor[*IndexerRequest, struct{}, struct{}](r, atype.Indexer, provider, name)) != nil } // LookupIndexer looks up an [Indexer] registered by [DefineIndexer]. // It returns nil if the model was not defined. -func LookupIndexer(provider, name string) Indexer { - return (*indexerActionDef)(core.LookupActionFor[*IndexerRequest, struct{}, struct{}](atype.Indexer, provider, name)) +func LookupIndexer(r *registry.Registry, provider, name string) Indexer { + return (*indexerActionDef)(core.LookupActionFor[*IndexerRequest, struct{}, struct{}](r, atype.Indexer, provider, name)) } // DefineRetriever registers the given retrieve function as an action, and returns a // [Retriever] that runs it. -func DefineRetriever(provider, name string, ret func(context.Context, *RetrieverRequest) (*RetrieverResponse, error)) *retrieverActionDef { - return (*retrieverActionDef)(core.DefineAction(provider, name, atype.Retriever, nil, ret)) +func DefineRetriever(r *registry.Registry, provider, name string, ret func(context.Context, *RetrieverRequest) (*RetrieverResponse, error)) *retrieverActionDef { + return (*retrieverActionDef)(core.DefineAction(r, provider, name, atype.Retriever, nil, ret)) } // IsDefinedRetriever reports whether a [Retriever] is defined. -func IsDefinedRetriever(provider, name string) bool { - return (*retrieverActionDef)(core.LookupActionFor[*RetrieverRequest, *RetrieverResponse, struct{}](atype.Retriever, provider, name)) != nil +func IsDefinedRetriever(r *registry.Registry, provider, name string) bool { + return (*retrieverActionDef)(core.LookupActionFor[*RetrieverRequest, *RetrieverResponse, struct{}](r, atype.Retriever, provider, name)) != nil } // LookupRetriever looks up a [Retriever] registered by [DefineRetriever]. // It returns nil if the model was not defined. -func LookupRetriever(provider, name string) Retriever { - return (*retrieverActionDef)(core.LookupActionFor[*RetrieverRequest, *RetrieverResponse, struct{}](atype.Retriever, provider, name)) +func LookupRetriever(r *registry.Registry, provider, name string) Retriever { + return (*retrieverActionDef)(core.LookupActionFor[*RetrieverRequest, *RetrieverResponse, struct{}](r, atype.Retriever, provider, name)) } // Index runs the given [Indexer]. diff --git a/go/ai/tools.go b/go/ai/tools.go index dcb379ec2..2cae9db65 100644 --- a/go/ai/tools.go +++ b/go/ai/tools.go @@ -51,13 +51,13 @@ type Tool interface { } // DefineTool defines a tool function. -func DefineTool[In, Out any](name, description string, fn func(ctx context.Context, input In) (Out, error)) *ToolDef[In, Out] { +func DefineTool[In, Out any](r *registry.Registry, name, description string, fn func(ctx context.Context, input In) (Out, error)) *ToolDef[In, Out] { metadata := make(map[string]any) metadata["type"] = "tool" metadata["name"] = name metadata["description"] = description - toolAction := core.DefineAction(provider, name, atype.Tool, metadata, fn) + toolAction := core.DefineAction(r, provider, name, atype.Tool, metadata, fn) return &ToolDef[In, Out]{ action: toolAction, @@ -125,6 +125,6 @@ func runAction(ctx context.Context, action Tool, input map[string]any) (any, err } // LookupTool looks up the tool in the registry by provided name and returns it. -func LookupTool(name string) Tool { - return &toolAction{action: registry.Global.LookupAction(fmt.Sprintf("/tool/local/%s", name))} +func LookupTool(r *registry.Registry, name string) Tool { + return &toolAction{action: r.LookupAction(fmt.Sprintf("/tool/local/%s", name))} } diff --git a/go/core/action.go b/go/core/action.go index 48757820b..b6900f27e 100644 --- a/go/core/action.go +++ b/go/core/action.go @@ -67,12 +67,13 @@ type noStream = func(context.Context, struct{}) error // DefineAction creates a new non-streaming Action and registers it. func DefineAction[In, Out any]( + r *registry.Registry, provider, name string, atype atype.ActionType, metadata map[string]any, fn func(context.Context, In) (Out, error), ) *Action[In, Out, struct{}] { - return DefineActionInRegistry(registry.Global, provider, name, atype, metadata, nil, + return defineAction(r, provider, name, atype, metadata, nil, func(ctx context.Context, in In, _ noStream) (Out, error) { return fn(ctx, in) }) @@ -80,17 +81,23 @@ func DefineAction[In, Out any]( // DefineStreamingAction creates a new streaming action and registers it. func DefineStreamingAction[In, Out, Stream any]( + r *registry.Registry, provider, name string, atype atype.ActionType, metadata map[string]any, fn Func[In, Out, Stream], ) *Action[In, Out, Stream] { - return DefineActionInRegistry(registry.Global, provider, name, atype, metadata, nil, fn) + return defineAction(r, provider, name, atype, metadata, nil, fn) } // DefineCustomAction defines a streaming action with type Custom. -func DefineCustomAction[In, Out, Stream any](provider, name string, metadata map[string]any, fn Func[In, Out, Stream]) *Action[In, Out, Stream] { - return DefineStreamingAction(provider, name, atype.Custom, metadata, fn) +func DefineCustomAction[In, Out, Stream any]( + r *registry.Registry, + provider, name string, + metadata map[string]any, + fn Func[In, Out, Stream], +) *Action[In, Out, Stream] { + return DefineStreamingAction(r, provider, name, atype.Custom, metadata, fn) } // DefineActionWithInputSchema creates a new Action and registers it. @@ -98,21 +105,21 @@ func DefineCustomAction[In, Out, Stream any](provider, name string, metadata map // defined dynamically; the static input type is "any". // This is used for prompts. func DefineActionWithInputSchema[Out any]( + r *registry.Registry, provider, name string, atype atype.ActionType, metadata map[string]any, inputSchema *jsonschema.Schema, fn func(context.Context, any) (Out, error), ) *Action[any, Out, struct{}] { - return DefineActionInRegistry(registry.Global, provider, name, atype, metadata, inputSchema, + return defineAction(r, provider, name, atype, metadata, inputSchema, func(ctx context.Context, in any, _ noStream) (Out, error) { return fn(ctx, in) }) } -// DefineActionInRegistry creates an action and registers it with the given Registry. -// For use by the Genkit module only. -func DefineActionInRegistry[In, Out, Stream any]( +// defineAction creates an action and registers it with the given Registry. +func defineAction[In, Out, Stream any]( r *registry.Registry, provider, name string, atype atype.ActionType, @@ -175,12 +182,7 @@ func (a *Action[In, Out, Stream]) Run(ctx context.Context, input In, cb func(con "output", fmt.Sprintf("%#v", output), "err", err) }() - tstate := a.tstate - if tstate == nil { - // This action has probably not been registered. - tstate = registry.Global.TracingState() - } - return tracing.RunInNewSpan(ctx, tstate, a.name, "action", false, input, + return tracing.RunInNewSpan(ctx, a.tstate, a.name, "action", false, input, func(ctx context.Context, input In) (Out, error) { start := time.Now() var err error @@ -259,9 +261,9 @@ func (a *Action[I, O, S]) Desc() action.Desc { // LookupActionFor returns the action for the given key in the global registry, // or nil if there is none. // It panics if the action is of the wrong type. -func LookupActionFor[In, Out, Stream any](typ atype.ActionType, provider, name string) *Action[In, Out, Stream] { +func LookupActionFor[In, Out, Stream any](r *registry.Registry, typ atype.ActionType, provider, name string) *Action[In, Out, Stream] { key := fmt.Sprintf("/%s/%s/%s", typ, provider, name) - a := registry.Global.LookupAction(key) + a := r.LookupAction(key) if a == nil { return nil } diff --git a/go/core/action_test.go b/go/core/action_test.go index e8c5edfd9..5bf24b5a0 100644 --- a/go/core/action_test.go +++ b/go/core/action_test.go @@ -30,7 +30,11 @@ func inc(_ context.Context, x int, _ noStream) (int, error) { } func TestActionRun(t *testing.T) { - a := newAction("inc", atype.Custom, nil, nil, inc) + r, err := registry.New() + if err != nil { + t.Fatal(err) + } + a := defineAction(r, "test", "inc", atype.Custom, nil, nil, inc) got, err := a.Run(context.Background(), 3, nil) if err != nil { t.Fatal(err) @@ -41,7 +45,11 @@ func TestActionRun(t *testing.T) { } func TestActionRunJSON(t *testing.T) { - a := newAction("inc", atype.Custom, nil, nil, inc) + r, err := registry.New() + if err != nil { + t.Fatal(err) + } + a := defineAction(r, "test", "inc", atype.Custom, nil, nil, inc) input := []byte("3") want := []byte("4") got, err := a.RunJSON(context.Background(), input, nil) @@ -67,7 +75,11 @@ func count(ctx context.Context, n int, cb func(context.Context, int) error) (int func TestActionStreaming(t *testing.T) { ctx := context.Background() - a := newAction("count", atype.Custom, nil, nil, count) + r, err := registry.New() + if err != nil { + t.Fatal(err) + } + a := defineAction(r, "test", "count", atype.Custom, nil, nil, count) const n = 3 // Non-streaming. @@ -98,10 +110,14 @@ func TestActionStreaming(t *testing.T) { } func TestActionTracing(t *testing.T) { + r, err := registry.New() + if err != nil { + t.Fatal(err) + } tc := tracing.NewTestOnlyTelemetryClient() - registry.Global.TracingState().WriteTelemetryImmediate(tc) + r.TracingState().WriteTelemetryImmediate(tc) const actionName = "TestTracing-inc" - a := newAction(actionName, atype.Custom, nil, nil, inc) + a := defineAction(r, "test", actionName, atype.Custom, nil, nil, inc) if _, err := a.Run(context.Background(), 3, nil); err != nil { t.Fatal(err) } diff --git a/go/core/core.go b/go/core/core.go index a5ff8c385..5895b8e92 100644 --- a/go/core/core.go +++ b/go/core/core.go @@ -30,6 +30,6 @@ import ( ) // RegisterSpanProcessor registers an OpenTelemetry SpanProcessor for tracing. -func RegisterSpanProcessor(sp sdktrace.SpanProcessor) { - registry.Global.RegisterSpanProcessor(sp) +func RegisterSpanProcessor(r *registry.Registry, sp sdktrace.SpanProcessor) { + r.RegisterSpanProcessor(sp) } diff --git a/go/genkit/flow.go b/go/genkit/flow.go index 6b0e7d838..52ede66c8 100644 --- a/go/genkit/flow.go +++ b/go/genkit/flow.go @@ -170,11 +170,12 @@ func WithLocalAuth(authContext AuthContext) FlowRunOption { // // fn takes an input of type In and returns an output of type Out. func DefineFlow[In, Out any]( + g *Genkit, name string, fn func(ctx context.Context, input In) (Out, error), opts ...FlowOption, ) *Flow[In, Out, struct{}] { - return defineFlow(registry.Global, name, core.Func[In, Out, struct{}]( + return defineFlow(g.reg, name, core.Func[In, Out, struct{}]( func(ctx context.Context, input In, cb func(ctx context.Context, _ struct{}) error) (Out, error) { return fn(ctx, input) }), opts...) @@ -190,11 +191,12 @@ func DefineFlow[In, Out any]( // with a final return value that includes all the streamed data. // Otherwise, it should ignore the callback and just return a result. func DefineStreamingFlow[In, Out, Stream any]( + g *Genkit, name string, fn func(ctx context.Context, input In, callback func(context.Context, Stream) error) (Out, error), opts ...FlowOption, ) *Flow[In, Out, Stream] { - return defineFlow(registry.Global, name, core.Func[In, Out, Stream](fn), opts...) + return defineFlow(g.reg, name, core.Func[In, Out, Stream](fn), opts...) } func defineFlow[In, Out, Stream any](r *registry.Registry, name string, fn core.Func[In, Out, Stream], opts ...FlowOption) *Flow[In, Out, Stream] { @@ -205,7 +207,6 @@ func defineFlow[In, Out, Stream any](r *registry.Registry, name string, fn core. fn: fn, inputSchema: base.InferJSONSchema(i), outputSchema: base.InferJSONSchema(o), - // TODO: set stateStore? } flowOpts := &flowOptions{} for _, opt := range opts { @@ -234,7 +235,7 @@ func defineFlow[In, Out, Stream any](r *registry.Registry, name string, fn core. } return &result, err } - core.DefineActionInRegistry(r, "", f.name, atype.Flow, metadata, nil, afunc) + core.DefineStreamingAction(r, "", f.name, atype.Flow, metadata, afunc) f.tstate = r.TracingState() r.RegisterFlow(f) return f diff --git a/go/genkit/flow_test.go b/go/genkit/flow_test.go index 02bf0df0c..589918229 100644 --- a/go/genkit/flow_test.go +++ b/go/genkit/flow_test.go @@ -22,7 +22,6 @@ import ( "testing" "github.com/firebase/genkit/go/core" - "github.com/firebase/genkit/go/internal/registry" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" ) @@ -32,7 +31,11 @@ func incFlow(_ context.Context, i int, _ noStream) (int, error) { } func TestFlowStart(t *testing.T) { - f := DefineStreamingFlow("inc", incFlow) + ai, err := New(nil) + if err != nil { + t.Fatal(err) + } + f := DefineStreamingFlow(ai, "inc", incFlow) ss, err := core.NewFileFlowStateStore(t.TempDir()) if err != nil { t.Fatal(err) @@ -58,13 +61,17 @@ func TestFlowStart(t *testing.T) { } func TestFlowRun(t *testing.T) { + ai, err := New(nil) + if err != nil { + t.Fatal(err) + } n := 0 stepf := func() (int, error) { n++ return n, nil } - flow := DefineFlow("run", func(ctx context.Context, s string) ([]int, error) { + flow := DefineFlow(ai, "run", func(ctx context.Context, s string) ([]int, error) { g1, err := Run(ctx, "s1", stepf) if err != nil { return nil, err @@ -91,11 +98,11 @@ func TestFlowRun(t *testing.T) { } func TestRunFlow(t *testing.T) { - reg, err := registry.New() + ai, err := New(nil) if err != nil { t.Fatal(err) } - f := defineFlow(reg, "inc", incFlow) + f := defineFlow(ai.reg, "inc", incFlow) got, err := f.Run(context.Background(), 2) if err != nil { t.Fatal(err) diff --git a/go/genkit/genkit.go b/go/genkit/genkit.go index d2d75cd3c..122edd376 100644 --- a/go/genkit/genkit.go +++ b/go/genkit/genkit.go @@ -22,14 +22,34 @@ import ( "net/http" "os" "os/signal" + "strings" "sync" "syscall" + "github.com/firebase/genkit/go/ai" "github.com/firebase/genkit/go/internal/registry" + "github.com/invopop/jsonschema" + + sdktrace "go.opentelemetry.io/otel/sdk/trace" ) -// Options are options to [Init]. +// Genkit encapsulates a Genkit instance including the registry and configuration. +type Genkit struct { + // The registry for this instance. + reg *registry.Registry + // Options to configure the instance. + Opts *Options +} + type Options struct { + // The default model to use if no model is specified. + DefaultModel string + // Directory where dotprompts are stored. + PromptDir string +} + +// StartOptions are options to [Start]. +type StartOptions struct { // If "-", do not start a FlowServer. // Otherwise, start a FlowServer on the given address, or the // default of ":3400" if empty. @@ -39,10 +59,31 @@ type Options struct { Flows []string } -// Init initializes Genkit. +// New creates a new Genkit instance. +func New(opts *Options) (*Genkit, error) { + r, err := registry.New() + if err != nil { + return nil, err + } + if opts == nil { + opts = &Options{} + } + if opts.DefaultModel != "" { + parts := strings.Split(opts.DefaultModel, "/") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid default model format %q, expected provider/name", opts.DefaultModel) + } + } + return &Genkit{ + reg: r, + Opts: opts, + }, nil +} + +// Start initializes Genkit. // After it is called, no further actions can be defined. // -// Init starts servers depending on the value of the GENKIT_ENV +// Start starts servers depending on the value of the GENKIT_ENV // environment variable and the provided options. // // If GENKIT_ENV = "dev", a development server is started @@ -50,16 +91,16 @@ type Options struct { // of ":3100" if empty. // // If opts.FlowAddr is a value other than "-", a flow server is started -// and the call to Init waits for the server to shut down. -// If opts.FlowAddr == "-", no flow server is started and Init returns immediately. +// and the call to Start waits for the server to shut down. +// If opts.FlowAddr == "-", no flow server is started and Start returns immediately. // -// Thus Init(nil) will start a dev server in the "dev" environment, will always start +// Thus Start(nil) will start a dev server in the "dev" environment, will always start // a flow server, and will pause execution until the flow server terminates. -func Init(ctx context.Context, opts *Options) error { +func (g *Genkit) Start(ctx context.Context, opts *StartOptions) error { if opts == nil { - opts = &Options{} + opts = &StartOptions{} } - registry.Global.Freeze() + g.reg.Freeze() var mu sync.Mutex var servers []*http.Server @@ -70,7 +111,7 @@ func Init(ctx context.Context, opts *Options) error { wg.Add(1) go func() { defer wg.Done() - s := startReflectionServer(ctx, errCh) + s := startReflectionServer(ctx, g.reg, errCh) mu.Lock() servers = append(servers, s) mu.Unlock() @@ -81,7 +122,7 @@ func Init(ctx context.Context, opts *Options) error { wg.Add(1) go func() { defer wg.Done() - s := startFlowServer(opts.FlowAddr, opts.Flows, errCh) + s := startFlowServer(g, opts.FlowAddr, opts.Flows, errCh) mu.Lock() servers = append(servers, s) mu.Unlock() @@ -120,3 +161,165 @@ func Init(ctx context.Context, opts *Options) error { return shutdownServers(servers) } + +// DefineModel registers the given generate function as an action, and returns a +// [Model] that runs it. +func DefineModel( + g *Genkit, + provider, name string, + metadata *ai.ModelMetadata, + generate func(context.Context, *ai.ModelRequest, ai.ModelStreamingCallback) (*ai.ModelResponse, error), +) ai.Model { + return ai.DefineModel(g.reg, provider, name, metadata, generate) +} + +// IsDefinedModel reports whether a model is defined. +func IsDefinedModel(g *Genkit, provider, name string) bool { + return ai.IsDefinedModel(g.reg, provider, name) +} + +// LookupModel looks up a [Model] registered by [DefineModel]. +// It returns nil if the model was not defined. +func LookupModel(g *Genkit, provider, name string) ai.Model { + return ai.LookupModel(g.reg, provider, name) +} + +// DefineTool defines a tool to be passed to a model generate call. +func DefineTool[In, Out any](g *Genkit, name, description string, fn func(ctx context.Context, input In) (Out, error)) *ai.ToolDef[In, Out] { + return ai.DefineTool(g.reg, name, description, fn) +} + +// LookupTool looks up the tool in the registry by provided name and returns it. +func LookupTool(g *Genkit, name string) ai.Tool { + return ai.LookupTool(g.reg, name) +} + +// DefinePrompt takes a function that renders a prompt template +// into a [GenerateRequest] that may be passed to a [Model]. +// The prompt expects some input described by inputSchema. +// DefinePrompt registers the function as an action, +// and returns a [Prompt] that runs it. +func DefinePrompt( + g *Genkit, + provider, name string, + metadata map[string]any, + inputSchema *jsonschema.Schema, + render func(context.Context, any) (*ai.ModelRequest, error), +) *ai.Prompt { + return ai.DefinePrompt(g.reg, provider, name, metadata, inputSchema, render) +} + +// IsDefinedPrompt reports whether a [Prompt] is defined. +func IsDefinedPrompt(g *Genkit, provider, name string) bool { + return ai.IsDefinedPrompt(g.reg, provider, name) +} + +// LookupPrompt looks up a [Prompt] registered by [DefinePrompt]. +// It returns nil if the prompt was not defined. +func LookupPrompt(g *Genkit, provider, name string) *ai.Prompt { + return ai.LookupPrompt(g.reg, provider, name) +} + +// Generate run generate request for this model. Returns ModelResponse struct. +func Generate(ctx context.Context, g *Genkit, opts ...ai.GenerateOption) (*ai.ModelResponse, error) { + opts, err := optsWithDefaults(g, opts) + if err != nil { + return nil, err + } + return ai.Generate(ctx, g.reg, opts...) +} + +// GenerateText run generate request for this model. Returns generated text only. +func GenerateText(ctx context.Context, g *Genkit, opts ...ai.GenerateOption) (string, error) { + opts, err := optsWithDefaults(g, opts) + if err != nil { + return "", err + } + return ai.GenerateText(ctx, g.reg, opts...) +} + +// GenerateData run generate request for this model. Returns ModelResponse struct and fills value with structured output. +func GenerateData(ctx context.Context, g *Genkit, value any, opts ...ai.GenerateOption) (*ai.ModelResponse, error) { + opts, err := optsWithDefaults(g, opts) + if err != nil { + return nil, err + } + return ai.GenerateData(ctx, g.reg, value, opts...) +} + +// GenerateWithRequest runs the model with the given request and streaming callback. +func GenerateWithRequest(ctx context.Context, g *Genkit, m ai.Model, req *ai.ModelRequest, cb ai.ModelStreamingCallback) (*ai.ModelResponse, error) { + return m.Generate(ctx, g.reg, req, cb) +} + +// DefineIndexer registers the given index function as an action, and returns an +// [Indexer] that runs it. +func DefineIndexer(g *Genkit, provider, name string, index func(context.Context, *ai.IndexerRequest) error) ai.Indexer { + return ai.DefineIndexer(g.reg, provider, name, index) +} + +// IsDefinedIndexer reports whether an [Indexer] is defined. +func IsDefinedIndexer(g *Genkit, provider, name string) bool { + return ai.IsDefinedIndexer(g.reg, provider, name) +} + +// LookupIndexer looks up an [Indexer] registered by [DefineIndexer]. +// It returns nil if the model was not defined. +func LookupIndexer(g *Genkit, provider, name string) ai.Indexer { + return ai.LookupIndexer(g.reg, provider, name) +} + +// DefineRetriever registers the given retrieve function as an action, and returns a +// [Retriever] that runs it. +func DefineRetriever(g *Genkit, provider, name string, ret func(context.Context, *ai.RetrieverRequest) (*ai.RetrieverResponse, error)) ai.Retriever { + return ai.DefineRetriever(g.reg, provider, name, ret) +} + +// IsDefinedRetriever reports whether a [Retriever] is defined. +func IsDefinedRetriever(g *Genkit, provider, name string) bool { + return ai.IsDefinedRetriever(g.reg, provider, name) +} + +// LookupRetriever looks up a [Retriever] registered by [DefineRetriever]. +// It returns nil if the model was not defined. +func LookupRetriever(g *Genkit, provider, name string) ai.Retriever { + return ai.LookupRetriever(g.reg, provider, name) +} + +// DefineEmbedder registers the given embed function as an action, and returns an +// [Embedder] that runs it. +func DefineEmbedder(g *Genkit, provider, name string, embed func(context.Context, *ai.EmbedRequest) (*ai.EmbedResponse, error)) ai.Embedder { + return ai.DefineEmbedder(g.reg, provider, name, embed) +} + +// IsDefinedEmbedder reports whether an embedder is defined. +func IsDefinedEmbedder(g *Genkit, provider, name string) bool { + return ai.IsDefinedEmbedder(g.reg, provider, name) +} + +// LookupEmbedder looks up an [Embedder] registered by [DefineEmbedder]. +// It returns nil if the embedder was not defined. +func LookupEmbedder(g *Genkit, provider, name string) ai.Embedder { + return ai.LookupEmbedder(g.reg, provider, name) +} + +// RegisterSpanProcessor registers an OpenTelemetry SpanProcessor for tracing. +func RegisterSpanProcessor(g *Genkit, sp sdktrace.SpanProcessor) { + g.reg.RegisterSpanProcessor(sp) +} + +// optsWithDefaults prepends defaults to the options so that they can be overridden by the caller. +func optsWithDefaults(g *Genkit, opts []ai.GenerateOption) ([]ai.GenerateOption, error) { + if g.Opts.DefaultModel != "" { + parts := strings.Split(g.Opts.DefaultModel, "/") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid default model format %q, expected provider/name", g.Opts.DefaultModel) + } + model := LookupModel(g, parts[0], parts[1]) + if model == nil { + return nil, fmt.Errorf("default model %q not found", g.Opts.DefaultModel) + } + opts = append([]ai.GenerateOption{ai.WithModel(model)}, opts...) + } + return opts, nil +} diff --git a/go/genkit/genkit_test.go b/go/genkit/genkit_test.go index adc818cdf..104ab2fe1 100644 --- a/go/genkit/genkit_test.go +++ b/go/genkit/genkit_test.go @@ -20,7 +20,11 @@ import ( ) func TestStreamFlow(t *testing.T) { - f := DefineStreamingFlow("count", count) + ai, err := New(nil) + if err != nil { + t.Fatal(err) + } + f := DefineStreamingFlow(ai, "count", count) iter := f.Stream(context.Background(), 2) want := 0 iter(func(val *StreamFlowValue[int, int], err error) bool { diff --git a/go/genkit/servers.go b/go/genkit/servers.go index ef7cd9455..433773090 100644 --- a/go/genkit/servers.go +++ b/go/genkit/servers.go @@ -63,10 +63,10 @@ type devServer struct { // startReflectionServer starts the Reflection API server listening at the // value of the environment variable GENKIT_REFLECTION_PORT for the port, // or ":3100" if it is empty. -func startReflectionServer(ctx context.Context, errCh chan<- error) *http.Server { +func startReflectionServer(ctx context.Context, r *registry.Registry, errCh chan<- error) *http.Server { slog.Debug("starting reflection server") addr := serverAddress("", "GENKIT_REFLECTION_PORT", "127.0.0.1:3100") - s := &devServer{reg: registry.Global} + s := &devServer{reg: r} if err := s.writeRuntimeFile(addr); err != nil { slog.Error("failed to write runtime file", "error", err) } @@ -162,10 +162,10 @@ func findProjectRoot() (string, error) { // for the port, and if that is empty it uses ":3400". // // To construct a server with additional routes, use [NewFlowServeMux]. -func startFlowServer(addr string, flows []string, errCh chan<- error) *http.Server { +func startFlowServer(g *Genkit, addr string, flows []string, errCh chan<- error) *http.Server { slog.Debug("starting flow server") addr = serverAddress(addr, "PORT", "127.0.0.1:3400") - mux := NewFlowServeMux(flows) + mux := NewFlowServeMux(g, flows) return startServer(addr, mux, errCh) } @@ -366,8 +366,8 @@ func (s *devServer) handleListActions(w http.ResponseWriter, r *http.Request) er // // mainMux := http.NewServeMux() // mainMux.Handle("POST /flow/", http.StripPrefix("/flow/", NewFlowServeMux())) -func NewFlowServeMux(flows []string) *http.ServeMux { - return newFlowServeMux(registry.Global, flows) +func NewFlowServeMux(g *Genkit, flows []string) *http.ServeMux { + return newFlowServeMux(g.reg, flows) } func newFlowServeMux(r *registry.Registry, flows []string) *http.ServeMux { diff --git a/go/genkit/servers_test.go b/go/genkit/servers_test.go index 8017a94b5..09378a931 100644 --- a/go/genkit/servers_test.go +++ b/go/genkit/servers_test.go @@ -34,11 +34,11 @@ import ( "github.com/invopop/jsonschema" ) -func inc(_ context.Context, x int, _ noStream) (int, error) { +func inc(_ context.Context, x int) (int, error) { return x + 1, nil } -func dec(_ context.Context, x int, _ noStream) (int, error) { +func dec(_ context.Context, x int) (int, error) { return x - 1, nil } @@ -50,12 +50,12 @@ func TestDevServer(t *testing.T) { tc := tracing.NewTestOnlyTelemetryClient() r.TracingState().WriteTelemetryImmediate(tc) - core.DefineActionInRegistry(r, "devServer", "inc", atype.Custom, map[string]any{ + core.DefineAction(r, "devServer", "inc", atype.Custom, map[string]any{ "foo": "bar", - }, nil, inc) - core.DefineActionInRegistry(r, "devServer", "dec", atype.Custom, map[string]any{ + }, inc) + core.DefineAction(r, "devServer", "dec", atype.Custom, map[string]any{ "bar": "baz", - }, nil, dec) + }, dec) srv := httptest.NewServer(newDevServeMux(&devServer{reg: r})) defer srv.Close() diff --git a/go/internal/doc-snippets/dotprompt.go b/go/internal/doc-snippets/dotprompt.go index 10b73fa88..8c6cff48f 100644 --- a/go/internal/doc-snippets/dotprompt.go +++ b/go/internal/doc-snippets/dotprompt.go @@ -18,24 +18,31 @@ import ( "context" "encoding/base64" "fmt" + "log" "os" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/dotprompt" "github.com/firebase/genkit/go/plugins/vertexai" ) func dot01() error { // [START dot01_1] - dotprompt.SetDirectory("prompts") - prompt, err := dotprompt.Open("greeting") + g, err := genkit.New(&genkit.Options{ + PromptDir: "prompts", + }) + if err != nil { + log.Fatal(err) + } + prompt, err := dotprompt.Open(g, "greeting") // [END dot01_1] // [START dot01_2] ctx := context.Background() // Default to the project in GCLOUD_PROJECT and the location "us-central1". - vertexai.Init(ctx, nil) + vertexai.Init(ctx, g, nil) // The .prompt file specifies vertexai/gemini-1.5-flash, which is // automatically defined by Init(). However, if it specified a model that @@ -54,7 +61,7 @@ func dot01() error { Name string `json:"name"` } response, err := prompt.Generate( - ctx, + ctx, g, dotprompt.WithInput(GreetingPromptInput{ Location: "the beach", Style: "a fancy pirate", @@ -81,7 +88,12 @@ func dot01() error { } func dot02() { - prompt, _ := dotprompt.Open("") + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + + prompt, _ := dotprompt.Open(g, "greeting") type GreetingPromptInput struct { Location string `json:"location"` Style string `json:"style"` @@ -90,10 +102,11 @@ func dot02() { // [START dot02] // Make sure you set up the model you're using. - vertexai.DefineModel("gemini-1.5-flash", nil) + vertexai.DefineModel(g, "gemini-1.5-flash", nil) response, err := prompt.Generate( context.Background(), + g, dotprompt.WithInput(GreetingPromptInput{ Location: "the beach", Style: "a fancy pirate", @@ -113,8 +126,13 @@ func dot02() { func dot03() error { // [START dot03] - dotprompt.SetDirectory("prompts") - describeImagePrompt, err := dotprompt.Open("describe_image") + g, err := genkit.New(&genkit.Options{ + PromptDir: "prompts", + }) + if err != nil { + log.Fatal(err) + } + describeImagePrompt, err := dotprompt.Open(g, "describe_image") if err != nil { return err } @@ -130,7 +148,7 @@ func dot03() error { PhotoUrl string `json:"photo_url"` } response, err := describeImagePrompt.Generate( - context.Background(), + context.Background(), g, dotprompt.WithInput(DescribeImagePromptInput{ PhotoUrl: dataURI, }), @@ -143,14 +161,26 @@ func dot03() error { } func dot04() { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + // [START dot04] - describeImagePrompt, err := dotprompt.OpenVariant("describe_image", "geminipro") + describeImagePrompt, err := dotprompt.OpenVariant(g, "describe_image", "geminipro") // [END dot04] _ = err _ = describeImagePrompt } func dot05() { + g, err := genkit.New(&genkit.Options{ + PromptDir: "prompts", + }) + if err != nil { + log.Fatal(err) + } + isBetaTester := func(user string) bool { return true } @@ -158,11 +188,10 @@ func dot05() { // [START dot05] var myPrompt *dotprompt.Prompt - var err error if isBetaTester(user) { - myPrompt, err = dotprompt.OpenVariant("describe_image", "geminipro") + myPrompt, err = dotprompt.OpenVariant(g, "describe_image", "geminipro") } else { - myPrompt, err = dotprompt.Open("describe_image") + myPrompt, err = dotprompt.Open(g, "describe_image") } // [END dot05] diff --git a/go/internal/doc-snippets/flows.go b/go/internal/doc-snippets/flows.go index b91f02e36..cfc45894f 100644 --- a/go/internal/doc-snippets/flows.go +++ b/go/internal/doc-snippets/flows.go @@ -27,8 +27,11 @@ import ( ) func f1() { + g, _ := genkit.New(nil) + // [START flow1] menuSuggestionFlow := genkit.DefineFlow( + g, "menuSuggestionFlow", func(ctx context.Context, restaurantTheme string) (string, error) { suggestion := makeMenuItemSuggestion(restaurantTheme) @@ -51,8 +54,11 @@ type MenuSuggestion struct { func makeMenuItemSuggestion(string) string { return "" } func f2() { + g, _ := genkit.New(nil) + // [START flow2] menuSuggestionFlow := genkit.DefineFlow( + g, "menuSuggestionFlow", func(ctx context.Context, restaurantTheme string) (MenuSuggestion, error) { suggestion := makeStructuredMenuItemSuggestion(restaurantTheme) @@ -76,8 +82,11 @@ type StreamType string // [END streaming-types] func f3() { + g, _ := genkit.New(nil) + // [START streaming] menuSuggestionFlow := genkit.DefineStreamingFlow( + g, "menuSuggestionFlow", func( ctx context.Context, @@ -130,14 +139,19 @@ func makeFullMenuSuggestion(restaurantTheme InputType, menuChunks chan StreamTyp // [START main] func main() { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } genkit.DefineFlow( + g, "menuSuggestionFlow", func(ctx context.Context, restaurantTheme string) (string, error) { // ... return "", nil }, ) - if err := genkit.Init(context.Background(), nil); err != nil { + if err := g.Start(context.Background(), nil); err != nil { log.Fatal(err) } } @@ -145,12 +159,18 @@ func main() { // [END main] func f4() { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + // [START mux] mainMux := http.NewServeMux() - mainMux.Handle("POST /flow/", http.StripPrefix("/flow/", genkit.NewFlowServeMux(nil))) + mainMux.Handle("POST /flow/", http.StripPrefix("/flow/", genkit.NewFlowServeMux(g, nil))) // [END mux] // [START run] genkit.DefineFlow( + g, "menuSuggestionFlow", func(ctx context.Context, restaurantTheme string) (string, error) { themes, err := genkit.Run(ctx, "find-similar-themes", func() (string, error) { @@ -166,9 +186,13 @@ func f4() { } func deploy(ctx context.Context) { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } // [START init] - if err := genkit.Init(ctx, - &genkit.Options{FlowAddr: ":3400"}, // Add this parameter. + if err := g.Start(ctx, + &genkit.StartOptions{FlowAddr: ":3400"}, // Add this parameter. ); err != nil { log.Fatal(err) } @@ -176,6 +200,10 @@ func deploy(ctx context.Context) { } func f5() { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } // [START auth] ctx := context.Background() // Define an auth policy and create a Firebase auth provider @@ -192,6 +220,7 @@ func f5() { } // Define a flow with authentication authenticatedFlow := genkit.DefineFlow( + g, "authenticated-flow", func(ctx context.Context, userID string) (string, error) { return fmt.Sprintf("Secure data for user %s", userID), nil @@ -203,6 +232,10 @@ func f5() { } func f6() { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } ctx := context.Background() var policy func(authContext genkit.AuthContext, input any) error required := true @@ -215,9 +248,9 @@ func f6() { return fmt.Sprintf("Secure data for user %s", userID), nil } // [START auth-define] - genkit.DefineFlow("secureUserFlow", userDataFunc, genkit.WithFlowAuth(firebaseAuth)) + genkit.DefineFlow(g, "secureUserFlow", userDataFunc, genkit.WithFlowAuth(firebaseAuth)) // [END auth-define] - authenticatedFlow := genkit.DefineFlow("your-flow", userDataFunc, genkit.WithFlowAuth(firebaseAuth)) + authenticatedFlow := genkit.DefineFlow(g, "your-flow", userDataFunc, genkit.WithFlowAuth(firebaseAuth)) // [START auth-run] response, err := authenticatedFlow.Run(ctx, "user123", genkit.WithLocalAuth(map[string]any{"UID": "user123"})) diff --git a/go/internal/doc-snippets/gcp.go b/go/internal/doc-snippets/gcp.go index 0dadcb36a..1efee223c 100644 --- a/go/internal/doc-snippets/gcp.go +++ b/go/internal/doc-snippets/gcp.go @@ -16,15 +16,22 @@ package snippets import ( "context" + "log" "log/slog" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/googlecloud" ) func gcpEx(ctx context.Context) error { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } // [START init] if err := googlecloud.Init( ctx, + g, googlecloud.Config{ProjectID: "your-google-cloud-project"}, ); err != nil { return err diff --git a/go/internal/doc-snippets/googleai.go b/go/internal/doc-snippets/googleai.go index 203a62537..ef9cee755 100644 --- a/go/internal/doc-snippets/googleai.go +++ b/go/internal/doc-snippets/googleai.go @@ -16,33 +16,38 @@ package snippets import ( "context" + "log" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/googleai" ) func googleaiEx(ctx context.Context) error { - var err error + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } // [START init] - if err := googleai.Init(ctx, nil); err != nil { + if err := googleai.Init(ctx, g, nil); err != nil { return err } // [END init] yourKey := "" // [START initkey] - if err := googleai.Init(ctx, &googleai.Config{APIKey: yourKey}); err != nil { + if err := googleai.Init(ctx, g, &googleai.Config{APIKey: yourKey}); err != nil { return err } // [END initkey] // [START model] - model := googleai.Model("gemini-1.5-flash") + model := googleai.Model(g, "gemini-1.5-flash") // [END model] // [START gen] - text, err := ai.GenerateText(ctx, model, ai.WithTextPrompt("Tell me a joke.")) + text, err := genkit.GenerateText(ctx, g, ai.WithModel(model), ai.WithTextPrompt("Tell me a joke.")) if err != nil { return err } @@ -53,7 +58,7 @@ func googleaiEx(ctx context.Context) error { var userInput string // [START embedder] - embeddingModel := googleai.Embedder("text-embedding-004") + embeddingModel := googleai.Embedder(g, "text-embedding-004") // [END embedder] // [START embed] diff --git a/go/internal/doc-snippets/init/main.go b/go/internal/doc-snippets/init/main.go index a66237a77..b68cf377e 100644 --- a/go/internal/doc-snippets/init/main.go +++ b/go/internal/doc-snippets/init/main.go @@ -30,25 +30,31 @@ import ( func main() { ctx := context.Background() + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + // Initialize the Google AI plugin. When you pass nil for the // Config parameter, the Google AI plugin will get the API key from the // GOOGLE_GENAI_API_KEY environment variable, which is the recommended // practice. - if err := googleai.Init(ctx, nil); err != nil { + if err := googleai.Init(ctx, g, nil); err != nil { log.Fatal(err) } // Define a simple flow that prompts an LLM to generate menu suggestions. - genkit.DefineFlow("menuSuggestionFlow", func(ctx context.Context, input string) (string, error) { + genkit.DefineFlow(g, "menuSuggestionFlow", func(ctx context.Context, input string) (string, error) { // The Google AI API provides access to several generative models. Here, // we specify gemini-1.5-flash. - m := googleai.Model("gemini-1.5-flash") + m := googleai.Model(g, "gemini-1.5-flash") if m == nil { return "", errors.New("menuSuggestionFlow: failed to find model") } // Construct a request and send it to the model API (Google AI). - resp, err := ai.Generate(ctx, m, + resp, err := genkit.Generate(ctx, g, + ai.WithModel(m), ai.WithConfig(&ai.GenerationCommonConfig{Temperature: 1}), ai.WithTextPrompt(fmt.Sprintf(`Suggest an item for the menu of a %s themed restaurant`, input))) if err != nil { @@ -67,7 +73,7 @@ func main() { // after all of your plug-in configuration and flow definitions. When you // pass a nil configuration to Init, Genkit starts a local flow server, // which you can interact with using the developer UI. - if err := genkit.Init(ctx, nil); err != nil { + if err := g.Start(ctx, nil); err != nil { log.Fatal(err) } } diff --git a/go/internal/doc-snippets/modelplugin/modelplugin.go b/go/internal/doc-snippets/modelplugin/modelplugin.go index 39a3b285c..cb14c02eb 100644 --- a/go/internal/doc-snippets/modelplugin/modelplugin.go +++ b/go/internal/doc-snippets/modelplugin/modelplugin.go @@ -19,6 +19,7 @@ import ( "fmt" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" ) const providerID = "mymodels" @@ -33,8 +34,13 @@ type MyModelConfig struct { // [END cfg] func Init() error { + g, err := genkit.New(nil) + if err != nil { + return err + } + // [START definemodel] - ai.DefineModel( + genkit.DefineModel(g, providerID, "my-model", &ai.ModelMetadata{ Label: "my-model", diff --git a/go/internal/doc-snippets/models.go b/go/internal/doc-snippets/models.go index e7edf5587..97709c89c 100644 --- a/go/internal/doc-snippets/models.go +++ b/go/internal/doc-snippets/models.go @@ -23,6 +23,7 @@ import ( // [START import] "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/vertexai" // [END import] ) @@ -33,21 +34,26 @@ var ctx = context.Background() var gemini15pro ai.Model func m1() error { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + // [START init] // Default to the value of GCLOUD_PROJECT for the project, // and "us-central1" for the location. // To specify these values directly, pass a vertexai.Config value to Init. - if err := vertexai.Init(ctx, nil); err != nil { + if err := vertexai.Init(ctx, g, nil); err != nil { return err } // [END init] // [START model] - model := vertexai.Model("gemini-1.5-flash") + model := vertexai.Model(g, "gemini-1.5-flash") // [END model] // [START call] - responseText, err := ai.GenerateText(ctx, model, ai.WithTextPrompt("Tell me a joke.")) + responseText, err := genkit.GenerateText(ctx, g, ai.WithModel(model), ai.WithTextPrompt("Tell me a joke.")) if err != nil { return err } @@ -57,10 +63,15 @@ func m1() error { } func opts() error { - model := vertexai.Model("gemini-1.5-flash") + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + model := vertexai.Model(g, "gemini-1.5-flash") // [START options] - response, err := ai.Generate(ctx, model, + response, err := genkit.Generate(ctx, g, + ai.WithModel(model), ai.WithTextPrompt("Tell me a joke about dogs."), ai.WithConfig(ai.GenerationCommonConfig{ Temperature: 1.67, @@ -77,8 +88,13 @@ func opts() error { } func streaming() error { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } // [START streaming] - response, err := ai.Generate(ctx, gemini15pro, + response, err := genkit.Generate(ctx, g, + ai.WithModel(gemini15pro), ai.WithTextPrompt("Tell a long story about robots and ninjas."), // stream callback ai.WithStreaming( @@ -98,6 +114,11 @@ func streaming() error { } func multi() error { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + // [START multimodal] imageBytes, err := os.ReadFile("img.jpg") if err != nil { @@ -105,10 +126,12 @@ func multi() error { } encodedImage := base64.StdEncoding.EncodeToString(imageBytes) - resp, err := ai.Generate(ctx, gemini15pro, ai.WithMessages( - ai.NewUserMessage( - ai.NewTextPart("Describe the following image."), - ai.NewMediaPart("", "data:image/jpeg;base64,"+encodedImage)))) + resp, err := genkit.Generate(ctx, g, + ai.WithModel(gemini15pro), + ai.WithMessages( + ai.NewUserMessage( + ai.NewTextPart("Describe the following image."), + ai.NewMediaPart("", "data:image/jpeg;base64,"+encodedImage)))) // [END multimodal] if err != nil { return err @@ -118,8 +141,13 @@ func multi() error { } func tools() error { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } // [START tools] - myJokeTool := ai.DefineTool( + myJokeTool := genkit.DefineTool( + g, "myJoke", "useful when you need a joke to tell", func(ctx context.Context, input *any) (string, error) { @@ -127,7 +155,8 @@ func tools() error { }, ) - response, err := ai.Generate(ctx, gemini15pro, + response, err := genkit.Generate(ctx, g, + ai.WithModel(gemini15pro), ai.WithTextPrompt("Tell me a joke."), ai.WithTools(myJokeTool)) // [END tools] @@ -136,6 +165,10 @@ func tools() error { } func history() error { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } var prompt string // [START hist1] history := []*ai.Message{{ @@ -143,7 +176,9 @@ func history() error { Role: ai.RoleUser, }} - response, err := ai.Generate(context.Background(), gemini15pro, ai.WithMessages(history...)) + response, err := genkit.Generate(ctx, g, + ai.WithModel(gemini15pro), + ai.WithMessages(history...)) // [END hist1] _ = err // [START hist2] @@ -156,7 +191,9 @@ func history() error { Role: ai.RoleUser, }) - response, err = ai.Generate(ctx, gemini15pro, ai.WithMessages(history...)) + response, err = genkit.Generate(ctx, g, + ai.WithModel(gemini15pro), + ai.WithMessages(history...)) // [END hist3] // [START hist4] history = []*ai.Message{{ diff --git a/go/internal/doc-snippets/ollama.go b/go/internal/doc-snippets/ollama.go index 38fe65cc7..76afebc02 100644 --- a/go/internal/doc-snippets/ollama.go +++ b/go/internal/doc-snippets/ollama.go @@ -16,13 +16,18 @@ package snippets import ( "context" + "log" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/ollama" ) func ollamaEx(ctx context.Context) error { - var err error + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } // [START init] // Init with Ollama's default local address. @@ -35,6 +40,7 @@ func ollamaEx(ctx context.Context) error { // [START definemodel] model := ollama.DefineModel( + g, ollama.ModelDefinition{ Name: "gemma2", Type: "chat", // "chat" or "generate" @@ -49,7 +55,9 @@ func ollamaEx(ctx context.Context) error { // [END definemodel] // [START gen] - text, err := ai.GenerateText(ctx, model, ai.WithTextPrompt("Tell me a joke.")) + text, err := genkit.GenerateText(ctx, g, + ai.WithModel(model), + ai.WithTextPrompt("Tell me a joke.")) if err != nil { return err } diff --git a/go/internal/doc-snippets/pinecone.go b/go/internal/doc-snippets/pinecone.go index 6ea83d902..9995c93c2 100644 --- a/go/internal/doc-snippets/pinecone.go +++ b/go/internal/doc-snippets/pinecone.go @@ -16,14 +16,19 @@ package snippets import ( "context" + "log" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/googleai" "github.com/firebase/genkit/go/plugins/pinecone" ) func pineconeEx(ctx context.Context) error { - var err error + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } // [START init] if err := pinecone.Init(ctx, ""); err != nil { @@ -39,9 +44,9 @@ func pineconeEx(ctx context.Context) error { // [END initkey] // [START defineindex] - menuIndexer, err := pinecone.DefineIndexer(ctx, pinecone.Config{ - IndexID: "menu_data", // Your Pinecone index - Embedder: googleai.Embedder("text-embedding-004"), // Embedding model of your choice + menuIndexer, err := pinecone.DefineIndexer(ctx, g, pinecone.Config{ + IndexID: "menu_data", // Your Pinecone index + Embedder: googleai.Embedder(g, "text-embedding-004"), // Embedding model of your choice }) if err != nil { return err @@ -60,9 +65,9 @@ func pineconeEx(ctx context.Context) error { // [END index] // [START defineretriever] - menuRetriever, err := pinecone.DefineRetriever(ctx, pinecone.Config{ - IndexID: "menu_data", // Your Pinecone index - Embedder: googleai.Embedder("text-embedding-004"), // Embedding model of your choice + menuRetriever, err := pinecone.DefineRetriever(ctx, g, pinecone.Config{ + IndexID: "menu_data", // Your Pinecone index + Embedder: googleai.Embedder(g, "text-embedding-004"), // Embedding model of your choice }) if err != nil { return err diff --git a/go/internal/doc-snippets/prompts.go b/go/internal/doc-snippets/prompts.go index ab066f1a1..c8bb0e3a3 100644 --- a/go/internal/doc-snippets/prompts.go +++ b/go/internal/doc-snippets/prompts.go @@ -18,16 +18,25 @@ import ( "context" "errors" "fmt" + "log" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/invopop/jsonschema" ) func pr01() { - model := ai.LookupModel("googleai", "gemini-1.5-flash") + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + + model := genkit.LookupModel(g, "googleai", "gemini-1.5-flash") // [START pr01] - ai.Generate(context.Background(), model, ai.WithTextPrompt("You are a helpful AI assistant named Walt.")) + genkit.Generate(context.Background(), g, + ai.WithModel(model), + ai.WithTextPrompt("You are a helpful AI assistant named Walt.")) // [END pr01] } @@ -40,10 +49,16 @@ func helloPrompt(name string) *ai.Part { // [END hello] func pr02() { - model := ai.LookupModel("googleai", "gemini-1.5-flash") + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + + model := genkit.LookupModel(g, "googleai", "gemini-1.5-flash") // [START pr02] - response, err := ai.GenerateText(context.Background(), model, + response, err := genkit.GenerateText(context.Background(), g, + ai.WithModel(model), ai.WithMessages(ai.NewUserMessage(helloPrompt("Fred")))) // [END pr02] @@ -53,13 +68,19 @@ func pr02() { } func pr03() error { - model := ai.LookupModel("googleai", "gemini-1.5-flash") + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + + model := genkit.LookupModel(g, "googleai", "gemini-1.5-flash") // [START pr03_1] type HelloPromptInput struct { UserName string } - helloPrompt := ai.DefinePrompt( + helloPrompt := genkit.DefinePrompt( + g, "prompts", "helloPrompt", nil, // Additional model config @@ -84,7 +105,7 @@ func pr03() error { if err != nil { return err } - response, err := model.Generate(context.Background(), request, nil) + response, err := genkit.GenerateWithRequest(context.Background(), g, model, request, nil) // [END pr03_2] _ = response diff --git a/go/internal/doc-snippets/rag/main.go b/go/internal/doc-snippets/rag/main.go index d2b8a4815..638100571 100644 --- a/go/internal/doc-snippets/rag/main.go +++ b/go/internal/doc-snippets/rag/main.go @@ -34,7 +34,12 @@ func main() { // [START vec] ctx := context.Background() - err := vertexai.Init(ctx, &vertexai.Config{}) + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + + err = vertexai.Init(ctx, g, &vertexai.Config{}) if err != nil { log.Fatal(err) } @@ -44,9 +49,10 @@ func main() { } menuPDFIndexer, _, err := localvec.DefineIndexerAndRetriever( + g, "menuQA", localvec.Config{ - Embedder: vertexai.Embedder("text-embedding-004"), + Embedder: vertexai.Embedder(g, "text-embedding-004"), }, ) if err != nil { @@ -61,6 +67,7 @@ func main() { // [END splitcfg] // [START indexflow] genkit.DefineFlow( + g, "indexMenu", func(ctx context.Context, path string) (any, error) { // Extract plain text from the PDF. Wrap the logic in Run so it @@ -97,7 +104,7 @@ func main() { ) // [END indexflow] - err = genkit.Init(ctx, nil) + err = g.Start(ctx, nil) if err != nil { log.Fatal(err) } @@ -133,7 +140,12 @@ func menuQA() { // [START retrieve] ctx := context.Background() - err := vertexai.Init(ctx, &vertexai.Config{}) + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + + err = vertexai.Init(ctx, g, &vertexai.Config{}) if err != nil { log.Fatal(err) } @@ -142,12 +154,13 @@ func menuQA() { log.Fatal(err) } - model := vertexai.Model("gemini-1.5-flash") + model := vertexai.Model(g, "gemini-1.5-flash") _, menuPdfRetriever, err := localvec.DefineIndexerAndRetriever( + g, "menuQA", localvec.Config{ - Embedder: vertexai.Embedder("text-embedding-004"), + Embedder: vertexai.Embedder(g, "text-embedding-004"), }, ) if err != nil { @@ -155,6 +168,7 @@ func menuQA() { } genkit.DefineFlow( + g, "menuQA", func(ctx context.Context, question string) (string, error) { // Retrieve text relevant to the user's question. @@ -173,7 +187,8 @@ func menuQA() { } // Call Generate, including the menu information in your prompt. - return ai.GenerateText(ctx, model, + return genkit.GenerateText(ctx, g, + ai.WithModel(model), ai.WithMessages( ai.NewSystemTextMessage(` You are acting as a helpful AI assistant that can answer questions about the @@ -187,10 +202,16 @@ make up an answer. Do not add or change items on the menu.`), } func customret() { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + _, menuPDFRetriever, _ := localvec.DefineIndexerAndRetriever( + g, "menuQA", localvec.Config{ - Embedder: vertexai.Embedder("text-embedding-004"), + Embedder: vertexai.Embedder(g, "text-embedding-004"), }, ) @@ -199,7 +220,8 @@ func customret() { K int PreRerankK int } - advancedMenuRetriever := ai.DefineRetriever( + advancedMenuRetriever := genkit.DefineRetriever( + g, "custom", "advancedMenuRetriever", func(ctx context.Context, req *ai.RetrieverRequest) (*ai.RetrieverResponse, error) { diff --git a/go/internal/doc-snippets/telemetryplugin/telemetryplugin.go b/go/internal/doc-snippets/telemetryplugin/telemetryplugin.go index d0f174fbc..855ca7cc4 100644 --- a/go/internal/doc-snippets/telemetryplugin/telemetryplugin.go +++ b/go/internal/doc-snippets/telemetryplugin/telemetryplugin.go @@ -21,7 +21,8 @@ import ( // [START import] // Import the Genkit core library. - "github.com/firebase/genkit/go/core" + + "github.com/firebase/genkit/go/genkit" // Import the OpenTelemetry libraries. "go.opentelemetry.io/otel" @@ -43,9 +44,15 @@ type Config struct { // Defaults to [slog.LevelInfo]. LogLevel slog.Leveler } + // [END config] func Init(cfg Config) error { + g, err := genkit.New(nil) + if err != nil { + return err + } + // [START shouldexport] shouldExport := cfg.ForceExport || os.Getenv("GENKIT_ENV") != "dev" if !shouldExport { @@ -55,7 +62,7 @@ func Init(cfg Config) error { // [START registerspanexporter] spanProcessor := trace.NewBatchSpanProcessor(YourCustomSpanExporter{}) - core.RegisterSpanProcessor(spanProcessor) + genkit.RegisterSpanProcessor(g, spanProcessor) // [END registerspanexporter] // [START registermetricexporter] diff --git a/go/internal/doc-snippets/vertexai.go b/go/internal/doc-snippets/vertexai.go index b86cddda9..d09f412e4 100644 --- a/go/internal/doc-snippets/vertexai.go +++ b/go/internal/doc-snippets/vertexai.go @@ -16,39 +16,46 @@ package snippets import ( "context" + "log" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/vertexai" ) func vertexaiEx(ctx context.Context) error { - var err error + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } // [START init] - if err := vertexai.Init(ctx, nil); err != nil { + if err := vertexai.Init(ctx, g, nil); err != nil { return err } // [END init] yourProjectID := "" // [START initproj] - if err := vertexai.Init(ctx, &vertexai.Config{ProjectID: yourProjectID}); err != nil { + if err := vertexai.Init(ctx, g, &vertexai.Config{ProjectID: yourProjectID}); err != nil { return err } // [END initproj] // [START initloc] - if err := vertexai.Init(ctx, &vertexai.Config{Location: "asia-south1"}); err != nil { + if err := vertexai.Init(ctx, g, &vertexai.Config{Location: "asia-south1"}); err != nil { return err } // [END initloc] // [START model] - langModel := vertexai.Model("gemini-1.5-flash") + langModel := vertexai.Model(g, "gemini-1.5-flash") // [END model] // [START gen] - genRes, err := ai.GenerateText(ctx, langModel, ai.WithTextPrompt("Tell me a joke.")) + genRes, err := genkit.GenerateText(ctx, g, + ai.WithModel(langModel), + ai.WithTextPrompt("Tell me a joke.")) if err != nil { return err } @@ -59,7 +66,7 @@ func vertexaiEx(ctx context.Context) error { var userInput string // [START embedder] - embeddingModel := vertexai.Embedder("text-embedding-004") + embeddingModel := vertexai.Embedder(g, "text-embedding-004") // [END embedder] // [START embed] diff --git a/go/internal/fakeembedder/fakeembedder_test.go b/go/internal/fakeembedder/fakeembedder_test.go index 51d8c0630..c01d037d4 100644 --- a/go/internal/fakeembedder/fakeembedder_test.go +++ b/go/internal/fakeembedder/fakeembedder_test.go @@ -20,11 +20,17 @@ import ( "testing" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/internal/registry" ) func TestFakeEmbedder(t *testing.T) { + r, err := registry.New() + if err != nil { + t.Fatal(err) + } + embed := New() - emb := ai.DefineEmbedder("fake", "embed", embed.Embed) + emb := ai.DefineEmbedder(r, "fake", "embed", embed.Embed) d := ai.DocumentFromText("fakeembedder test", nil) vals := []float32{1, 2} diff --git a/go/internal/registry/registry.go b/go/internal/registry/registry.go index da8348ab8..804898a0f 100644 --- a/go/internal/registry/registry.go +++ b/go/internal/registry/registry.go @@ -16,7 +16,6 @@ package registry import ( "fmt" - "log" "log/slog" "os" "slices" @@ -31,19 +30,6 @@ import ( // This file implements registries of actions and other values. -// The global registry, used in non-test code. -// A test may create their own registries to avoid conflicting with other tests. -var Global *Registry - -func init() { - // Initialize the global registry, along with a dev tracer, at program startup. - var err error - Global, err = New() - if err != nil { - log.Fatal(err) - } -} - type Registry struct { tstate *tracing.State mu sync.Mutex diff --git a/go/plugins/dotprompt/dotprompt.go b/go/plugins/dotprompt/dotprompt.go index ea9116540..e667aa58d 100644 --- a/go/plugins/dotprompt/dotprompt.go +++ b/go/plugins/dotprompt/dotprompt.go @@ -30,19 +30,12 @@ import ( "github.com/aymerick/raymond" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/internal/base" "github.com/invopop/jsonschema" "gopkg.in/yaml.v3" ) -// promptDirectory is the directory where dotprompt files are found. -var promptDirectory string - -// SetDirectory sets the directory where dotprompt files are read from. -func SetDirectory(directory string) { - promptDirectory = directory -} - // Prompt is a parsed dotprompt file. // // A dotprompt file consists of YAML frontmatter within --- lines, @@ -117,18 +110,18 @@ type PromptOption func(p *Prompt) error // Open opens and parses a dotprompt file. // The name is a base file name, without the ".prompt" extension. -func Open(name string) (*Prompt, error) { - return OpenVariant(name, "") +func Open(g *genkit.Genkit, name string) (*Prompt, error) { + return OpenVariant(g, name, "") } // OpenVariant opens a parses a dotprompt file with a variant. // If the variant does not exist, the non-variant version is tried. -func OpenVariant(name, variant string) (*Prompt, error) { - if promptDirectory == "" { +func OpenVariant(g *genkit.Genkit, name, variant string) (*Prompt, error) { + if g.Opts.PromptDir == "" { // The TypeScript code defaults to ./prompts, // but that makes the program change behavior // depending on where it is run. - return nil, errors.New("missing call to dotprompt.SetDirectory") + return nil, errors.New("PromptDir in Genkit options is empty") } vname := name @@ -136,19 +129,19 @@ func OpenVariant(name, variant string) (*Prompt, error) { vname = name + "." + variant } - fileName := filepath.Join(promptDirectory, vname+".prompt") + fileName := filepath.Join(g.Opts.PromptDir, vname+".prompt") data, err := os.ReadFile(fileName) if err != nil { if variant != "" && errors.Is(err, fs.ErrNotExist) { slog.Warn("prompt not found, trying without variant", "name", name, "variant", variant) - return OpenVariant(name, "") + return OpenVariant(g, name, "") } return nil, fmt.Errorf("failed to read dotprompt file %q: %w", name, err) } - return Parse(name, variant, data) + return Parse(g, name, variant, data) } // frontmatterYAML is the type we use to unpack the frontmatter. @@ -175,13 +168,13 @@ type frontmatterYAML struct { } // Parse parses the contents of a dotprompt file. -func Parse(name, variant string, data []byte) (*Prompt, error) { +func Parse(g *genkit.Genkit, name, variant string, data []byte) (*Prompt, error) { const header = "---\n" var fmName string var cfg Config if bytes.HasPrefix(data, []byte(header)) { var err error - fmName, cfg, data, err = parseFrontmatter(data[len(header):]) + fmName, cfg, data, err = parseFrontmatter(g, data[len(header):]) if err != nil { return nil, err } @@ -214,7 +207,7 @@ func newPrompt(name, templateText, hash string, config Config) (*Prompt, error) // parseFrontmatter parses the initial YAML frontmatter of a dotprompt file. // It returns the frontmatter as a Config along with the remaining data. -func parseFrontmatter(data []byte) (name string, c Config, rest []byte, err error) { +func parseFrontmatter(g *genkit.Genkit, data []byte) (name string, c Config, rest []byte, err error) { const footer = "\n---\n" end := bytes.Index(data, []byte(footer)) if end == -1 { @@ -228,7 +221,7 @@ func parseFrontmatter(data []byte) (name string, c Config, rest []byte, err erro var tools []ai.Tool for _, tn := range fy.Tools { - tools = append(tools, ai.LookupTool(tn)) + tools = append(tools, genkit.LookupTool(g, tn)) } ret := Config{ @@ -284,8 +277,8 @@ func parseFrontmatter(data []byte) (name string, c Config, rest []byte, err erro // Define creates and registers a new Prompt. This can be called from code that // doesn't have a prompt file. -func Define(name, templateText string, opts ...PromptOption) (*Prompt, error) { - p, err := New(name, templateText, Config{}) +func Define(g *genkit.Genkit, name, templateText string, opts ...PromptOption) (*Prompt, error) { + p, err := New(name, templateText, Config{ModelName: g.Opts.DefaultModel}) if err != nil { return nil, err } @@ -297,9 +290,7 @@ func Define(name, templateText string, opts ...PromptOption) (*Prompt, error) { } } - // TODO Inherit model from genkit instance - - p.Register() + p.Register(g) return p, nil } diff --git a/go/plugins/dotprompt/dotprompt_test.go b/go/plugins/dotprompt/dotprompt_test.go index a7fdf3be8..f177409da 100644 --- a/go/plugins/dotprompt/dotprompt_test.go +++ b/go/plugins/dotprompt/dotprompt_test.go @@ -17,9 +17,11 @@ package dotprompt import ( "context" "encoding/json" + "log" "testing" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/google/go-cmp/cmp" "github.com/invopop/jsonschema" ) @@ -28,8 +30,8 @@ type InputOutput struct { Text string `json:"text"` } -func testTool(name string) *ai.ToolDef[struct{ Test string }, string] { - return ai.DefineTool(name, "use when need to execute a test", +func testTool(g *genkit.Genkit, name string) *ai.ToolDef[struct{ Test string }, string] { + return genkit.DefineTool(g, name, "use when need to execute a test", func(ctx context.Context, input struct { Test string }) (string, error) { @@ -38,10 +40,19 @@ func testTool(name string) *ai.ToolDef[struct{ Test string }, string] { ) } -var testModel = ai.DefineModel("defineoptions", "test", nil, testGenerate) +var g, _ = genkit.New(&genkit.Options{ + PromptDir: "testdata", +}) + +var testModel = genkit.DefineModel(g, "defineoptions", "test", nil, testGenerate) func TestPrompts(t *testing.T) { - SetDirectory("testdata") + g, err := genkit.New(&genkit.Options{ + PromptDir: "testdata", + }) + if err != nil { + log.Fatal(err) + } var tests = []struct { name string @@ -126,7 +137,7 @@ func TestPrompts(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - prompt, err := Open(test.name) + prompt, err := Open(g, test.name) if err != nil { t.Fatal(err) } @@ -154,9 +165,10 @@ func TestPrompts(t *testing.T) { func TestOptionsPatternDefine(t *testing.T) { t.Run("WithTypesAndModel", func(t *testing.T) { dotPrompt, err := Define( + g, "TestTypes", "TestTypes", - WithTools(testTool("testOptionsPatternDefine")), + WithTools(testTool(g, "testOptionsPatternDefine")), WithDefaultConfig(&ai.GenerationCommonConfig{}), WithInputType(InputOutput{}), WithOutputType(InputOutput{}), @@ -199,6 +211,7 @@ func TestOptionsPatternDefine(t *testing.T) { t.Run("WithDefaultMap", func(t *testing.T) { dotPrompt, err := Define( + g, "TestDefaultMap", "TestDefaultMap", WithInputType(map[string]any{"test": "test"}), @@ -219,6 +232,7 @@ func TestOptionsPatternDefine(t *testing.T) { t.Run("WithDefaultStruct", func(t *testing.T) { dotPrompt, err := Define( + g, "TestDefaultStruct", "TestDefaultStruct", WithInputType(InputOutput{Text: "test"}), @@ -269,6 +283,7 @@ func TestOutputFormat(t *testing.T) { if test.output == nil { _, err = Define( + g, "aModel", "aModel", WithInputType(InputOutput{Text: "test"}), @@ -276,6 +291,7 @@ func TestOutputFormat(t *testing.T) { ) } else { _, err = Define( + g, "bModel", "bModel", WithInputType(InputOutput{Text: "test"}), @@ -355,12 +371,14 @@ func TestInputFormat(t *testing.T) { if test.inputType != nil { p, err = Define( + g, test.name, test.templateText, WithInputType(test.inputType), ) } else { p, err = Define( + g, "inputFormat", test.templateText, ) @@ -389,7 +407,7 @@ func TestPromptOptions(t *testing.T) { }{ { name: "WithTools", - with: WithTools(testTool("testPromptOptions")), + with: WithTools(testTool(g, "testPromptOptions")), }, { name: "WithDefaultConfig", @@ -424,6 +442,7 @@ func TestPromptOptions(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { _, err := Define( + g, "TestWith", "TestWith", test.with, diff --git a/go/plugins/dotprompt/genkit.go b/go/plugins/dotprompt/genkit.go index 866a929aa..520878934 100644 --- a/go/plugins/dotprompt/genkit.go +++ b/go/plugins/dotprompt/genkit.go @@ -24,6 +24,7 @@ import ( "github.com/firebase/genkit/go/ai" "github.com/firebase/genkit/go/core/tracing" + "github.com/firebase/genkit/go/genkit" ) // PromptRequest is a request to execute a dotprompt template and @@ -144,7 +145,7 @@ func (p *Prompt) buildRequest(ctx context.Context, input any) (*ai.ModelRequest, } // Register registers an action to render a prompt. -func (p *Prompt) Register() error { +func (p *Prompt) Register(g *genkit.Genkit) error { if p.prompt != nil { return nil } @@ -170,7 +171,7 @@ func (p *Prompt) Register() error { "template": p.TemplateText, }, } - p.prompt = ai.DefinePrompt("dotprompt", name, metadata, p.Config.InputSchema, p.buildRequest) + p.prompt = genkit.DefinePrompt(g, "dotprompt", name, metadata, p.Config.InputSchema, p.buildRequest) return nil } @@ -180,7 +181,7 @@ func (p *Prompt) Register() error { // the prompt. // // This implements the [ai.Prompt] interface. -func (p *Prompt) Generate(ctx context.Context, opts ...GenerateOption) (*ai.ModelResponse, error) { +func (p *Prompt) Generate(ctx context.Context, g *genkit.Genkit, opts ...GenerateOption) (*ai.ModelResponse, error) { tracing.SetCustomMetadataAttr(ctx, "subtype", "prompt") var pr PromptRequest @@ -232,13 +233,13 @@ func (p *Prompt) Generate(ctx context.Context, opts ...GenerateOption) (*ai.Mode return nil, errors.New("dotprompt model not in provider/name format") } - model = ai.LookupModel(provider, name) + model = genkit.LookupModel(g, provider, name) if model == nil { return nil, fmt.Errorf("no model named %q for provider %q", name, provider) } } - resp, err := model.Generate(ctx, mr, pr.Stream) + resp, err := genkit.GenerateWithRequest(ctx, g, model, mr, pr.Stream) if err != nil { return nil, err } @@ -247,8 +248,8 @@ func (p *Prompt) Generate(ctx context.Context, opts ...GenerateOption) (*ai.Mode } // GenerateText runs generate request for this prompt. Returns generated text only. -func (p *Prompt) GenerateText(ctx context.Context, opts ...GenerateOption) (string, error) { - res, err := p.Generate(ctx, opts...) +func (p *Prompt) GenerateText(ctx context.Context, g *genkit.Genkit, opts ...GenerateOption) (string, error) { + res, err := p.Generate(ctx, g, opts...) if err != nil { return "", err } @@ -258,14 +259,14 @@ func (p *Prompt) GenerateText(ctx context.Context, opts ...GenerateOption) (stri // GenerateData runs generate request for this prompt. Returns ModelResponse struct. // TODO: Stream GenerateData with partial JSON -func (p *Prompt) GenerateData(ctx context.Context, value any, opts ...GenerateOption) (*ai.ModelResponse, error) { +func (p *Prompt) GenerateData(ctx context.Context, g *genkit.Genkit, value any, opts ...GenerateOption) (*ai.ModelResponse, error) { with := WithOutputType(value) err := with(p) if err != nil { return nil, err } - resp, err := p.Generate(ctx, opts...) + resp, err := p.Generate(ctx, g, opts...) if err != nil { return nil, err } diff --git a/go/plugins/dotprompt/genkit_test.go b/go/plugins/dotprompt/genkit_test.go index 9b439710b..840e2e871 100644 --- a/go/plugins/dotprompt/genkit_test.go +++ b/go/plugins/dotprompt/genkit_test.go @@ -17,9 +17,11 @@ package dotprompt import ( "context" "fmt" + "log" "testing" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/google/go-cmp/cmp" ) @@ -49,13 +51,17 @@ func testGenerate(ctx context.Context, req *ai.ModelRequest, cb func(context.Con } func TestExecute(t *testing.T) { - testModel := ai.DefineModel("test", "test", nil, testGenerate) + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + testModel := genkit.DefineModel(g, "test", "test", nil, testGenerate) t.Run("Model", func(t *testing.T) { p, err := New("TestExecute", "TestExecute", Config{Model: testModel}) if err != nil { t.Fatal(err) } - resp, err := p.Generate(context.Background()) + resp, err := p.Generate(context.Background(), g) if err != nil { t.Fatal(err) } @@ -66,7 +72,7 @@ func TestExecute(t *testing.T) { if err != nil { t.Fatal(err) } - resp, err := p.Generate(context.Background()) + resp, err := p.Generate(context.Background(), g) if err != nil { t.Fatal(err) } @@ -77,7 +83,7 @@ func TestExecute(t *testing.T) { if err != nil { t.Fatal(err) } - resp, err := p.GenerateText(context.Background()) + resp, err := p.GenerateText(context.Background(), g) if err != nil { t.Fatal(err) } @@ -90,7 +96,7 @@ func TestExecute(t *testing.T) { if err != nil { t.Fatal(err) } - resp, err := p.GenerateData(context.Background(), InputOutput{}) + resp, err := p.GenerateData(context.Background(), g, InputOutput{}) if err != nil { t.Fatal(err) } @@ -100,10 +106,14 @@ func TestExecute(t *testing.T) { } func TestOptionsPatternGenerate(t *testing.T) { - testModel := ai.DefineModel("options", "test", nil, testGenerate) + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + testModel := genkit.DefineModel(g, "options", "test", nil, testGenerate) t.Run("Streaming", func(t *testing.T) { - p, err := Define("TestExecute", "TestExecute", WithInputType(InputOutput{})) + p, err := Define(g, "TestExecute", "TestExecute", WithInputType(InputOutput{})) if err != nil { t.Fatal(err) } @@ -111,6 +121,7 @@ func TestOptionsPatternGenerate(t *testing.T) { streamText := "" resp, err := p.Generate( context.Background(), + g, WithInput(InputOutput{ Text: "testing", }), @@ -132,13 +143,14 @@ func TestOptionsPatternGenerate(t *testing.T) { }) t.Run("WithModelName", func(t *testing.T) { - p, err := Define("TestModelname", "TestModelname", WithInputType(InputOutput{})) + p, err := Define(g, "TestModelname", "TestModelname", WithInputType(InputOutput{})) if err != nil { t.Fatal(err) } resp, err := p.Generate( context.Background(), + g, WithInput(InputOutput{ Text: "testing", }), @@ -153,7 +165,11 @@ func TestOptionsPatternGenerate(t *testing.T) { } func TestGenerateOptions(t *testing.T) { - p, err := Define("TestWithGenerate", "TestWithGenerate", WithInputType(InputOutput{})) + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + p, err := Define(g, "TestWithGenerate", "TestWithGenerate", WithInputType(InputOutput{})) if err != nil { t.Fatal(err) } @@ -188,7 +204,7 @@ func TestGenerateOptions(t *testing.T) { t.Run(test.name, func(t *testing.T) { _, err = p.Generate( context.Background(), - test.with, + g, test.with, ) diff --git a/go/plugins/dotprompt/render_test.go b/go/plugins/dotprompt/render_test.go index 58917ad7e..f85b9c259 100644 --- a/go/plugins/dotprompt/render_test.go +++ b/go/plugins/dotprompt/render_test.go @@ -16,14 +16,23 @@ package dotprompt import ( "fmt" + "log" "testing" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/google/go-cmp/cmp" ) // TestRender is some of the tests from prompt_test.ts. func TestRender(t *testing.T) { + g, err := genkit.New(&genkit.Options{ + PromptDir: "testdata", + }) + if err != nil { + log.Fatal(err) + } + var tests = []struct { prompt string input map[string]any @@ -62,7 +71,7 @@ This is the rest of the prompt`, for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - prompt, err := Parse(t.Name(), "", []byte(test.prompt)) + prompt, err := Parse(g, t.Name(), "", []byte(test.prompt)) if err != nil { if test.bad { t.Logf("got expected error %v", err) @@ -86,6 +95,13 @@ This is the rest of the prompt`, // TestRenderMessages is some of the tests from template_test.ts. func TestRenderMessages(t *testing.T) { + g, err := genkit.New(&genkit.Options{ + PromptDir: "testdata", + }) + if err != nil { + log.Fatal(err) + } + var tests = []struct { name string template string @@ -212,7 +228,7 @@ func TestRenderMessages(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - prompt, err := Parse(t.Name(), "", []byte(test.template)) + prompt, err := Parse(g, t.Name(), "", []byte(test.template)) if err != nil { t.Fatal(err) } diff --git a/go/plugins/googleai/googleai.go b/go/plugins/googleai/googleai.go index 20561e94a..f0e98fc50 100644 --- a/go/plugins/googleai/googleai.go +++ b/go/plugins/googleai/googleai.go @@ -26,6 +26,7 @@ import ( "sync" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/internal" "github.com/firebase/genkit/go/plugins/internal/gemini" "github.com/firebase/genkit/go/plugins/internal/uri" @@ -69,7 +70,7 @@ type Config struct { // Init initializes the plugin and all known models and embedders. // After calling Init, you may call [DefineModel] and [DefineEmbedder] to create // and register any additional generative models and embedders -func Init(ctx context.Context, cfg *Config) (err error) { +func Init(ctx context.Context, g *genkit.Genkit, cfg *Config) (err error) { if cfg == nil { cfg = &Config{} } @@ -108,10 +109,10 @@ func Init(ctx context.Context, cfg *Config) (err error) { state.pclient = client state.initted = true for model, caps := range knownCaps { - defineModel(model, caps) + defineModel(g, model, caps) } for _, e := range knownEmbedders { - defineEmbedder(e) + defineEmbedder(g, e) } return nil } @@ -122,7 +123,7 @@ func Init(ctx context.Context, cfg *Config) (err error) { // The second argument describes the capability of the model. // Use [IsDefinedModel] to determine if a model is already defined. // After [Init] is called, only the known models are defined. -func DefineModel(name string, caps *ai.ModelCapabilities) (ai.Model, error) { +func DefineModel(g *genkit.Genkit, name string, caps *ai.ModelCapabilities) (ai.Model, error) { state.mu.Lock() defer state.mu.Unlock() if !state.initted { @@ -138,16 +139,16 @@ func DefineModel(name string, caps *ai.ModelCapabilities) (ai.Model, error) { } else { mc = *caps } - return defineModel(name, mc), nil + return defineModel(g, name, mc), nil } // requires state.mu -func defineModel(name string, caps ai.ModelCapabilities) ai.Model { +func defineModel(g *genkit.Genkit, name string, caps ai.ModelCapabilities) ai.Model { meta := &ai.ModelMetadata{ Label: labelPrefix + " - " + name, Supports: caps, } - return ai.DefineModel(provider, name, meta, func( + return genkit.DefineModel(g, provider, name, meta, func( ctx context.Context, input *ai.ModelRequest, cb func(context.Context, *ai.ModelResponseChunk) error, @@ -157,8 +158,8 @@ func defineModel(name string, caps ai.ModelCapabilities) ai.Model { } // IsDefinedModel reports whether the named [Model] is defined by this plugin. -func IsDefinedModel(name string) bool { - return ai.IsDefinedModel(provider, name) +func IsDefinedModel(g *genkit.Genkit, name string) bool { + return genkit.IsDefinedModel(g, provider, name) } //copy:stop @@ -166,25 +167,25 @@ func IsDefinedModel(name string) bool { //copy:start vertexai.go defineEmbedder // DefineEmbedder defines an embedder with a given name. -func DefineEmbedder(name string) ai.Embedder { +func DefineEmbedder(g *genkit.Genkit, name string) ai.Embedder { state.mu.Lock() defer state.mu.Unlock() if !state.initted { panic(provider + ".Init not called") } - return defineEmbedder(name) + return defineEmbedder(g, name) } // IsDefinedEmbedder reports whether the named [Embedder] is defined by this plugin. -func IsDefinedEmbedder(name string) bool { - return ai.IsDefinedEmbedder(provider, name) +func IsDefinedEmbedder(g *genkit.Genkit, name string) bool { + return genkit.IsDefinedEmbedder(g, provider, name) } //copy:stop // requires state.mu -func defineEmbedder(name string) ai.Embedder { - return ai.DefineEmbedder(provider, name, func(ctx context.Context, input *ai.EmbedRequest) (*ai.EmbedResponse, error) { +func defineEmbedder(g *genkit.Genkit, name string) ai.Embedder { + return genkit.DefineEmbedder(g, provider, name, func(ctx context.Context, input *ai.EmbedRequest) (*ai.EmbedResponse, error) { em := state.pclient.EmbeddingModel(name) // TODO: set em.TaskType from EmbedRequest.Options? batch := em.NewBatch() @@ -211,14 +212,14 @@ func defineEmbedder(name string) ai.Embedder { // Model returns the [ai.Model] with the given name. // It returns nil if the model was not defined. -func Model(name string) ai.Model { - return ai.LookupModel(provider, name) +func Model(g *genkit.Genkit, name string) ai.Model { + return genkit.LookupModel(g, provider, name) } // Embedder returns the [ai.Embedder] with the given name. // It returns nil if the embedder was not defined. -func Embedder(name string) ai.Embedder { - return ai.LookupEmbedder(provider, name) +func Embedder(g *genkit.Genkit, name string) ai.Embedder { + return genkit.LookupEmbedder(g, provider, name) } //copy:stop diff --git a/go/plugins/googleai/googleai_test.go b/go/plugins/googleai/googleai_test.go index 01e6804ee..5ba1e12dc 100644 --- a/go/plugins/googleai/googleai_test.go +++ b/go/plugins/googleai/googleai_test.go @@ -18,6 +18,7 @@ import ( "context" "flag" "fmt" + "log" "math" "net/http" "net/http/httptest" @@ -26,6 +27,7 @@ import ( "testing" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/internal" "github.com/firebase/genkit/go/plugins/googleai" "google.golang.org/api/option" @@ -47,17 +49,22 @@ func TestLive(t *testing.T) { if *testAll { t.Skip("-all provided") } + g, err := genkit.New(&genkit.Options{ + DefaultModel: "googleai/gemini-1.5-flash", + }) + if err != nil { + log.Fatal(err) + } ctx := context.Background() - err := googleai.Init(ctx, &googleai.Config{APIKey: *apiKey}) + err = googleai.Init(ctx, g, &googleai.Config{APIKey: *apiKey}) if err != nil { t.Fatal(err) } - embedder := googleai.Embedder("embedding-001") - model := googleai.Model("gemini-1.0-pro") + embedder := googleai.Embedder(g, "embedding-001") if err != nil { t.Fatal(err) } - gablorkenTool := ai.DefineTool("gablorken", "use when need to calculate a gablorken", + gablorkenTool := genkit.DefineTool(g, "gablorken", "use when need to calculate a gablorken", func(ctx context.Context, input struct { Value float64 Over float64 @@ -85,7 +92,7 @@ func TestLive(t *testing.T) { } }) t.Run("generate", func(t *testing.T) { - resp, err := ai.Generate(ctx, model, ai.WithTextPrompt("Which country was Napoleon the emperor of?")) + resp, err := genkit.Generate(ctx, g, ai.WithTextPrompt("Which country was Napoleon the emperor of?")) if err != nil { t.Fatal(err) } @@ -104,7 +111,7 @@ func TestLive(t *testing.T) { t.Run("streaming", func(t *testing.T) { out := "" parts := 0 - final, err := ai.Generate(ctx, model, + final, err := genkit.Generate(ctx, g, ai.WithTextPrompt("Write one paragraph about the North Pole."), ai.WithStreaming(func(ctx context.Context, c *ai.ModelResponseChunk) error { parts++ @@ -134,7 +141,7 @@ func TestLive(t *testing.T) { } }) t.Run("tool", func(t *testing.T) { - resp, err := ai.Generate(ctx, model, + resp, err := genkit.Generate(ctx, g, ai.WithTextPrompt("what is a gablorken of 2 over 3.5?"), ai.WithTools(gablorkenTool)) @@ -151,6 +158,12 @@ func TestLive(t *testing.T) { } func TestHeader(t *testing.T) { + g, err := genkit.New(&genkit.Options{ + DefaultModel: "googleai/gemini-1.5-flash", + }) + if err != nil { + log.Fatal(err) + } if !*header { t.Skip("skipped; to run, pass -header and don't run the live test") } @@ -163,11 +176,10 @@ func TestHeader(t *testing.T) { defer server.Close() opts := []option.ClientOption{option.WithHTTPClient(server.Client()), option.WithEndpoint(server.URL)} - if err := googleai.Init(ctx, &googleai.Config{APIKey: "x", ClientOptions: opts}); err != nil { + if err := googleai.Init(ctx, g, &googleai.Config{APIKey: "x", ClientOptions: opts}); err != nil { t.Fatal(err) } - model := googleai.Model("gemini-1.0-pro") - _, _ = ai.Generate(ctx, model, ai.WithTextPrompt("hi")) + _, _ = genkit.Generate(ctx, g, ai.WithTextPrompt("hi")) got := header.Get("x-goog-api-client") want := regexp.MustCompile(fmt.Sprintf(`\bgenkit-go/%s\b`, internal.Version)) if !want.MatchString(got) { diff --git a/go/plugins/googlecloud/googlecloud.go b/go/plugins/googlecloud/googlecloud.go index ac612dd2b..a2ccadc8c 100644 --- a/go/plugins/googlecloud/googlecloud.go +++ b/go/plugins/googlecloud/googlecloud.go @@ -29,7 +29,7 @@ import ( "cloud.google.com/go/logging" mexporter "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric" texporter "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace" - "github.com/firebase/genkit/go/core" + "github.com/firebase/genkit/go/genkit" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" @@ -55,7 +55,7 @@ type Config struct { // Init initializes all telemetry in this package. // In the dev environment, this does nothing unless [Options.ForceExport] is true. -func Init(ctx context.Context, cfg Config) (err error) { +func Init(ctx context.Context, g *genkit.Genkit, cfg Config) (err error) { defer func() { if err != nil { err = fmt.Errorf("googlecloud.Init: %w", err) @@ -75,7 +75,7 @@ func Init(ctx context.Context, cfg Config) (err error) { return err } aexp := &adjustingTraceExporter{texp} - core.RegisterSpanProcessor(sdktrace.NewBatchSpanProcessor(aexp)) + genkit.RegisterSpanProcessor(g, sdktrace.NewBatchSpanProcessor(aexp)) if err := setMeterProvider(cfg.ProjectID, cfg.MetricInterval); err != nil { return err } diff --git a/go/plugins/localvec/localvec.go b/go/plugins/localvec/localvec.go index f6f1f86f3..f0858f892 100644 --- a/go/plugins/localvec/localvec.go +++ b/go/plugins/localvec/localvec.go @@ -32,6 +32,7 @@ import ( "github.com/firebase/genkit/go/ai" "github.com/firebase/genkit/go/core/logger" + "github.com/firebase/genkit/go/genkit" ) const provider = "devLocalVectorStore" @@ -48,35 +49,35 @@ func Init() error { return nil } // DefineIndexerAndRetriever defines an Indexer and Retriever that share the same underlying storage. // The name uniquely identifies the the Indexer and Retriever in the registry. -func DefineIndexerAndRetriever(name string, cfg Config) (ai.Indexer, ai.Retriever, error) { +func DefineIndexerAndRetriever(g *genkit.Genkit, name string, cfg Config) (ai.Indexer, ai.Retriever, error) { ds, err := newDocStore(cfg.Dir, name, cfg.Embedder, cfg.EmbedderOptions) if err != nil { return nil, nil, err } - return ai.DefineIndexer(provider, name, ds.index), - ai.DefineRetriever(provider, name, ds.retrieve), + return genkit.DefineIndexer(g, provider, name, ds.index), + genkit.DefineRetriever(g, provider, name, ds.retrieve), nil } // IsDefinedIndexer reports whether the named [Indexer] is defined by this plugin. -func IsDefinedIndexer(name string) bool { - return ai.IsDefinedIndexer(provider, name) +func IsDefinedIndexer(g *genkit.Genkit, name string) bool { + return genkit.IsDefinedIndexer(g, provider, name) } // Indexer returns the registered indexer with the given name. -func Indexer(name string) ai.Indexer { - return ai.LookupIndexer(provider, name) +func Indexer(g *genkit.Genkit, name string) ai.Indexer { + return genkit.LookupIndexer(g, provider, name) } // IsDefinedRetriever reports whether the named [Retriever] is defined by this plugin. -func IsDefinedRetriever(name string) bool { - return ai.IsDefinedRetriever(provider, name) +func IsDefinedRetriever(g *genkit.Genkit, name string) bool { + return genkit.IsDefinedRetriever(g, provider, name) } // Retriever returns the retriever with the given name. // The name must match the [Config.Name] value passed to [Init]. -func Retriever(name string) ai.Retriever { - return ai.LookupRetriever(provider, name) +func Retriever(g *genkit.Genkit, name string) ai.Retriever { + return genkit.LookupRetriever(g, provider, name) } // docStore implements a local vector database. diff --git a/go/plugins/localvec/localvec_test.go b/go/plugins/localvec/localvec_test.go index ce5f1505f..a479d27fe 100644 --- a/go/plugins/localvec/localvec_test.go +++ b/go/plugins/localvec/localvec_test.go @@ -21,12 +21,18 @@ import ( "testing" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/internal/fakeembedder" ) func TestLocalVec(t *testing.T) { ctx := context.Background() + g, err := genkit.New(nil) + if err != nil { + t.Fatal(err) + } + // Make two very similar vectors and one different vector. // Arrange for a fake embedder to return those vector // when provided with documents. @@ -50,7 +56,7 @@ func TestLocalVec(t *testing.T) { embedder.Register(d1, v1) embedder.Register(d2, v2) embedder.Register(d3, v3) - embedAction := ai.DefineEmbedder("fake", "embedder1", embedder.Embed) + embedAction := genkit.DefineEmbedder(g, "fake", "embedder1", embedder.Embed) ds, err := newDocStore(t.TempDir(), "testLocalVec", embedAction, nil) if err != nil { t.Fatal(err) @@ -92,6 +98,11 @@ func TestLocalVec(t *testing.T) { func TestPersistentIndexing(t *testing.T) { ctx := context.Background() + g, err := genkit.New(nil) + if err != nil { + t.Fatal(err) + } + const dim = 32 v1 := make([]float32, dim) v2 := make([]float32, dim) @@ -110,7 +121,7 @@ func TestPersistentIndexing(t *testing.T) { embedder.Register(d1, v1) embedder.Register(d2, v2) embedder.Register(d3, v3) - embedAction := ai.DefineEmbedder("fake", "embedder2", embedder.Embed) + embedAction := genkit.DefineEmbedder(g, "fake", "embedder2", embedder.Embed) tDir := t.TempDir() @@ -188,12 +199,16 @@ func TestSimilarity(t *testing.T) { } func TestInit(t *testing.T) { - embedder := ai.DefineEmbedder("fake", "e", fakeembedder.New().Embed) + g, err := genkit.New(nil) + if err != nil { + t.Fatal(err) + } + embedder := genkit.DefineEmbedder(g, "fake", "e", fakeembedder.New().Embed) if err := Init(); err != nil { t.Fatal(err) } const name = "mystore" - ind, ret, err := DefineIndexerAndRetriever(name, Config{Embedder: embedder}) + ind, ret, err := DefineIndexerAndRetriever(g, name, Config{Embedder: embedder}) if err != nil { t.Fatal(err) } diff --git a/go/plugins/ollama/embed.go b/go/plugins/ollama/embed.go index 051514420..b6d3cf2ef 100644 --- a/go/plugins/ollama/embed.go +++ b/go/plugins/ollama/embed.go @@ -23,6 +23,7 @@ import ( "strings" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" ) type EmbedOptions struct { @@ -125,13 +126,13 @@ func concatenateText(doc *ai.Document) string { } // DefineEmbedder defines an embedder with a given server address. -func DefineEmbedder(serverAddress string, model string) ai.Embedder { +func DefineEmbedder(g *genkit.Genkit, serverAddress string, model string) ai.Embedder { state.mu.Lock() defer state.mu.Unlock() if !state.initted { panic("ollama.Init not called") } - return ai.DefineEmbedder(provider, serverAddress, func(ctx context.Context, req *ai.EmbedRequest) (*ai.EmbedResponse, error) { + return genkit.DefineEmbedder(g, provider, serverAddress, func(ctx context.Context, req *ai.EmbedRequest) (*ai.EmbedResponse, error) { if req.Options == nil { req.Options = &EmbedOptions{Model: model} } @@ -143,13 +144,13 @@ func DefineEmbedder(serverAddress string, model string) ai.Embedder { } // IsDefinedEmbedder reports whether the embedder with the given server address is defined by this plugin. -func IsDefinedEmbedder(serverAddress string) bool { - isDefined := ai.IsDefinedEmbedder(provider, serverAddress) +func IsDefinedEmbedder(g *genkit.Genkit, serverAddress string) bool { + isDefined := genkit.IsDefinedEmbedder(g, provider, serverAddress) return isDefined } // Embedder returns the [ai.Embedder] with the given server address. // It returns nil if the embedder was not defined. -func Embedder(serverAddress string) ai.Embedder { - return ai.LookupEmbedder(provider, serverAddress) +func Embedder(g *genkit.Genkit, serverAddress string) ai.Embedder { + return genkit.LookupEmbedder(g, provider, serverAddress) } diff --git a/go/plugins/ollama/ollama.go b/go/plugins/ollama/ollama.go index bab805973..7d1bad962 100644 --- a/go/plugins/ollama/ollama.go +++ b/go/plugins/ollama/ollama.go @@ -30,6 +30,7 @@ import ( "time" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/internal/uri" ) @@ -47,7 +48,7 @@ var state struct { serverAddress string } -func DefineModel(model ModelDefinition, caps *ai.ModelCapabilities) ai.Model { +func DefineModel(g *genkit.Genkit, model ModelDefinition, caps *ai.ModelCapabilities) ai.Model { state.mu.Lock() defer state.mu.Unlock() if !state.initted { @@ -67,20 +68,20 @@ func DefineModel(model ModelDefinition, caps *ai.ModelCapabilities) ai.Model { Label: "Ollama - " + model.Name, Supports: mc, } - g := &generator{model: model, serverAddress: state.serverAddress} - return ai.DefineModel(provider, model.Name, meta, g.generate) + gen := &generator{model: model, serverAddress: state.serverAddress} + return genkit.DefineModel(g, provider, model.Name, meta, gen.generate) } // IsDefinedModel reports whether a model is defined. -func IsDefinedModel(name string) bool { - return ai.IsDefinedModel(provider, name) +func IsDefinedModel(g *genkit.Genkit, name string) bool { + return genkit.IsDefinedModel(g, provider, name) } // Model returns the [ai.Model] with the given name. // It returns nil if the model was not configured. -func Model(name string) ai.Model { - return ai.LookupModel(provider, name) +func Model(g *genkit.Genkit, name string) ai.Model { + return genkit.LookupModel(g, provider, name) } // ModelDefinition represents a model with its name and type. diff --git a/go/plugins/ollama/ollama_live_test.go b/go/plugins/ollama/ollama_live_test.go index 08c834fb6..ccce9c537 100644 --- a/go/plugins/ollama/ollama_live_test.go +++ b/go/plugins/ollama/ollama_live_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" ollamaPlugin "github.com/firebase/genkit/go/plugins/ollama" ) @@ -39,8 +40,13 @@ func TestLive(t *testing.T) { ctx := context.Background() + g, err := genkit.New(nil) + if err != nil { + t.Fatal(err) + } + // Initialize the Ollama plugin - err := ollamaPlugin.Init(ctx, &ollamaPlugin.Config{ + err = ollamaPlugin.Init(ctx, &ollamaPlugin.Config{ ServerAddress: *serverAddress, }) if err != nil { @@ -48,16 +54,16 @@ func TestLive(t *testing.T) { } // Define the model - ollamaPlugin.DefineModel(ollamaPlugin.ModelDefinition{Name: *modelName}, nil) + ollamaPlugin.DefineModel(g, ollamaPlugin.ModelDefinition{Name: *modelName}, nil) // Use the Ollama model - m := ollamaPlugin.Model(*modelName) + m := ollamaPlugin.Model(g, *modelName) if m == nil { t.Fatalf(`failed to find model: %s`, *modelName) } // Generate a response from the model - resp, err := m.Generate(ctx, + resp, err := genkit.GenerateWithRequest(ctx, g, m, ai.NewModelRequest( &ai.GenerationCommonConfig{Temperature: 1}, ai.NewUserTextMessage("I'm hungry, what should I eat?")), diff --git a/go/plugins/pinecone/genkit.go b/go/plugins/pinecone/genkit.go index efee773c9..ab946b3c4 100644 --- a/go/plugins/pinecone/genkit.go +++ b/go/plugins/pinecone/genkit.go @@ -27,6 +27,7 @@ import ( "time" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" ) const provider = "pinecone" @@ -83,31 +84,31 @@ type Config struct { } // DefineIndexer defines an Indexer with the given configuration. -func DefineIndexer(ctx context.Context, cfg Config) (ai.Indexer, error) { +func DefineIndexer(ctx context.Context, g *genkit.Genkit, cfg Config) (ai.Indexer, error) { ds, err := newDocStore(ctx, cfg) if err != nil { return nil, err } - return ai.DefineIndexer(provider, cfg.IndexID, ds.Index), nil + return genkit.DefineIndexer(g, provider, cfg.IndexID, ds.Index), nil } // DefineRetriever defines a Retriever with the given configuration. -func DefineRetriever(ctx context.Context, cfg Config) (ai.Retriever, error) { +func DefineRetriever(ctx context.Context, g *genkit.Genkit, cfg Config) (ai.Retriever, error) { ds, err := newDocStore(ctx, cfg) if err != nil { return nil, err } - return ai.DefineRetriever(provider, cfg.IndexID, ds.Retrieve), nil + return genkit.DefineRetriever(g, provider, cfg.IndexID, ds.Retrieve), nil } // IsDefinedIndexer reports whether the named [Indexer] is defined by this plugin. -func IsDefinedIndexer(name string) bool { - return ai.IsDefinedIndexer(provider, name) +func IsDefinedIndexer(g *genkit.Genkit, name string) bool { + return genkit.IsDefinedIndexer(g, provider, name) } // IsDefinedRetriever reports whether the named [Retriever] is defined by this plugin. -func IsDefinedRetriever(name string) bool { - return ai.IsDefinedRetriever(provider, name) +func IsDefinedRetriever(g *genkit.Genkit, name string) bool { + return genkit.IsDefinedRetriever(g, provider, name) } func newDocStore(ctx context.Context, cfg Config) (*docStore, error) { @@ -143,13 +144,13 @@ func newDocStore(ctx context.Context, cfg Config) (*docStore, error) { } // Indexer returns the indexer with the given index name. -func Indexer(name string) ai.Indexer { - return ai.LookupIndexer(provider, name) +func Indexer(g *genkit.Genkit, name string) ai.Indexer { + return genkit.LookupIndexer(g, provider, name) } // Retriever returns the retriever with the given index name. -func Retriever(name string) ai.Retriever { - return ai.LookupRetriever(provider, name) +func Retriever(g *genkit.Genkit, name string) ai.Retriever { + return genkit.LookupRetriever(g, provider, name) } // IndexerOptions may be passed in the Options field diff --git a/go/plugins/pinecone/genkit_test.go b/go/plugins/pinecone/genkit_test.go index db03832e4..d032dc281 100644 --- a/go/plugins/pinecone/genkit_test.go +++ b/go/plugins/pinecone/genkit_test.go @@ -20,6 +20,7 @@ import ( "testing" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/internal/fakeembedder" ) @@ -36,6 +37,11 @@ func TestGenkit(t *testing.T) { ctx := context.Background() + g, err := genkit.New(nil) + if err != nil { + t.Fatal(err) + } + // Get information about the index. client, err := newClient(ctx, *testAPIKey) @@ -77,13 +83,13 @@ func TestGenkit(t *testing.T) { } cfg := Config{ IndexID: *testIndex, - Embedder: ai.DefineEmbedder("fake", "embedder3", embedder.Embed), + Embedder: genkit.DefineEmbedder(g, "fake", "embedder3", embedder.Embed), } - indexer, err := DefineIndexer(ctx, cfg) + indexer, err := DefineIndexer(ctx, g, cfg) if err != nil { t.Fatal(err) } - retriever, err := DefineRetriever(ctx, cfg) + retriever, err := DefineRetriever(ctx, g, cfg) if err != nil { t.Fatal(err) } diff --git a/go/plugins/vertexai/vertexai.go b/go/plugins/vertexai/vertexai.go index b3215e03e..3cb95e2c4 100644 --- a/go/plugins/vertexai/vertexai.go +++ b/go/plugins/vertexai/vertexai.go @@ -25,6 +25,7 @@ import ( aiplatform "cloud.google.com/go/aiplatform/apiv1" "cloud.google.com/go/vertexai/genai" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/internal" "github.com/firebase/genkit/go/plugins/internal/gemini" "github.com/firebase/genkit/go/plugins/internal/uri" @@ -79,7 +80,7 @@ type Config struct { // Init initializes the plugin and all known models and embedders. // After calling Init, you may call [DefineModel] and [DefineEmbedder] to create // and register any additional generative models and embedders -func Init(ctx context.Context, cfg *Config) error { +func Init(ctx context.Context, g *genkit.Genkit, cfg *Config) error { if cfg == nil { cfg = &Config{} } @@ -124,10 +125,10 @@ func Init(ctx context.Context, cfg *Config) error { } state.initted = true for model, caps := range knownCaps { - defineModel(model, caps) + defineModel(g, model, caps) } for _, e := range knownEmbedders { - defineEmbedder(e) + defineEmbedder(g, e) } return nil } @@ -139,7 +140,7 @@ func Init(ctx context.Context, cfg *Config) error { // The second argument describes the capability of the model. // Use [IsDefinedModel] to determine if a model is already defined. // After [Init] is called, only the known models are defined. -func DefineModel(name string, caps *ai.ModelCapabilities) (ai.Model, error) { +func DefineModel(g *genkit.Genkit, name string, caps *ai.ModelCapabilities) (ai.Model, error) { state.mu.Lock() defer state.mu.Unlock() if !state.initted { @@ -155,16 +156,16 @@ func DefineModel(name string, caps *ai.ModelCapabilities) (ai.Model, error) { } else { mc = *caps } - return defineModel(name, mc), nil + return defineModel(g, name, mc), nil } // requires state.mu -func defineModel(name string, caps ai.ModelCapabilities) ai.Model { +func defineModel(g *genkit.Genkit, name string, caps ai.ModelCapabilities) ai.Model { meta := &ai.ModelMetadata{ Label: labelPrefix + " - " + name, Supports: caps, } - return ai.DefineModel(provider, name, meta, func( + return genkit.DefineModel(g, provider, name, meta, func( ctx context.Context, input *ai.ModelRequest, cb func(context.Context, *ai.ModelResponseChunk) error, @@ -174,8 +175,8 @@ func defineModel(name string, caps ai.ModelCapabilities) ai.Model { } // IsDefinedModel reports whether the named [Model] is defined by this plugin. -func IsDefinedModel(name string) bool { - return ai.IsDefinedModel(provider, name) +func IsDefinedModel(g *genkit.Genkit, name string) bool { + return genkit.IsDefinedModel(g, provider, name) } // DO NOT MODIFY above ^^^^ @@ -185,27 +186,27 @@ func IsDefinedModel(name string) bool { // DO NOT MODIFY below vvvv // DefineEmbedder defines an embedder with a given name. -func DefineEmbedder(name string) ai.Embedder { +func DefineEmbedder(g *genkit.Genkit, name string) ai.Embedder { state.mu.Lock() defer state.mu.Unlock() if !state.initted { panic(provider + ".Init not called") } - return defineEmbedder(name) + return defineEmbedder(g, name) } // IsDefinedEmbedder reports whether the named [Embedder] is defined by this plugin. -func IsDefinedEmbedder(name string) bool { - return ai.IsDefinedEmbedder(provider, name) +func IsDefinedEmbedder(g *genkit.Genkit, name string) bool { + return genkit.IsDefinedEmbedder(g, provider, name) } // DO NOT MODIFY above ^^^^ //copy:endsink defineEmbedder // requires state.mu -func defineEmbedder(name string) ai.Embedder { +func defineEmbedder(g *genkit.Genkit, name string) ai.Embedder { fullName := fmt.Sprintf("projects/%s/locations/%s/publishers/google/models/%s", state.projectID, state.location, name) - return ai.DefineEmbedder(provider, name, func(ctx context.Context, req *ai.EmbedRequest) (*ai.EmbedResponse, error) { + return genkit.DefineEmbedder(g, provider, name, func(ctx context.Context, req *ai.EmbedRequest) (*ai.EmbedResponse, error) { return embed(ctx, fullName, state.pclient, req) }) } @@ -215,14 +216,14 @@ func defineEmbedder(name string) ai.Embedder { // Model returns the [ai.Model] with the given name. // It returns nil if the model was not defined. -func Model(name string) ai.Model { - return ai.LookupModel(provider, name) +func Model(g *genkit.Genkit, name string) ai.Model { + return genkit.LookupModel(g, provider, name) } // Embedder returns the [ai.Embedder] with the given name. // It returns nil if the embedder was not defined. -func Embedder(name string) ai.Embedder { - return ai.LookupEmbedder(provider, name) +func Embedder(g *genkit.Genkit, name string) ai.Embedder { + return genkit.LookupEmbedder(g, provider, name) } // DO NOT MODIFY above ^^^^ diff --git a/go/plugins/vertexai/vertexai_test.go b/go/plugins/vertexai/vertexai_test.go index 1b2bb565f..fd322ee9e 100644 --- a/go/plugins/vertexai/vertexai_test.go +++ b/go/plugins/vertexai/vertexai_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/vertexai" ) @@ -36,15 +37,19 @@ func TestLive(t *testing.T) { t.Skipf("no -projectid provided") } ctx := context.Background() - const modelName = "gemini-1.0-pro" - err := vertexai.Init(ctx, &vertexai.Config{ProjectID: *projectID, Location: *location}) + g, err := genkit.New(&genkit.Options{ + DefaultModel: "vertexai/gemini-1.5-flash", + }) + if err != nil { + t.Fatal(err) + } + err = vertexai.Init(ctx, g, &vertexai.Config{ProjectID: *projectID, Location: *location}) if err != nil { t.Fatal(err) } - model := vertexai.Model(modelName) - embedder := vertexai.Embedder("textembedding-gecko@003") + embedder := vertexai.Embedder(g, "textembedding-gecko@003") - gablorkenTool := ai.DefineTool("gablorken", "use when need to calculate a gablorken", + gablorkenTool := genkit.DefineTool(g, "gablorken", "use when need to calculate a gablorken", func(ctx context.Context, input struct { Value float64 Over float64 @@ -53,7 +58,7 @@ func TestLive(t *testing.T) { }, ) t.Run("model", func(t *testing.T) { - resp, err := ai.Generate(ctx, model, ai.WithTextPrompt("Which country was Napoleon the emperor of?")) + resp, err := genkit.Generate(ctx, g, ai.WithTextPrompt("Which country was Napoleon the emperor of?")) if err != nil { t.Fatal(err) } @@ -71,8 +76,7 @@ func TestLive(t *testing.T) { t.Run("streaming", func(t *testing.T) { out := "" parts := 0 - model := vertexai.Model(modelName) - final, err := ai.Generate(ctx, model, + final, err := genkit.Generate(ctx, g, ai.WithTextPrompt("Write one paragraph about the Golden State Warriors."), ai.WithStreaming(func(ctx context.Context, c *ai.ModelResponseChunk) error { parts++ @@ -105,7 +109,7 @@ func TestLive(t *testing.T) { } }) t.Run("tool", func(t *testing.T) { - resp, err := ai.Generate(ctx, model, + resp, err := genkit.Generate(ctx, g, ai.WithTextPrompt("what is a gablorken of 2 over 3.5?"), ai.WithTools(gablorkenTool)) if err != nil { diff --git a/go/plugins/weaviate/weaviate.go b/go/plugins/weaviate/weaviate.go index c41e84e2d..40df346ea 100644 --- a/go/plugins/weaviate/weaviate.go +++ b/go/plugins/weaviate/weaviate.go @@ -23,6 +23,7 @@ import ( "sync" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/weaviate/weaviate-go-client/v4/weaviate" "github.com/weaviate/weaviate-go-client/v4/weaviate/auth" "github.com/weaviate/weaviate-go-client/v4/weaviate/graphql" @@ -143,7 +144,7 @@ type ClassConfig struct { // that use the same class. // The name uniquely identifies the Indexer and Retriever // in the registry. -func DefineIndexerAndRetriever(ctx context.Context, cfg ClassConfig) (ai.Indexer, ai.Retriever, error) { +func DefineIndexerAndRetriever(ctx context.Context, g *genkit.Genkit, cfg ClassConfig) (ai.Indexer, ai.Retriever, error) { if cfg.Embedder == nil { return nil, nil, errors.New("weaviate: Embedder required") } @@ -155,8 +156,8 @@ func DefineIndexerAndRetriever(ctx context.Context, cfg ClassConfig) (ai.Indexer if err != nil { return nil, nil, err } - indexer := ai.DefineIndexer(provider, cfg.Class, ds.Index) - retriever := ai.DefineRetriever(provider, cfg.Class, ds.Retrieve) + indexer := genkit.DefineIndexer(g, provider, cfg.Class, ds.Index) + retriever := genkit.DefineRetriever(g, provider, cfg.Class, ds.Retrieve) return indexer, retriever, nil } @@ -200,13 +201,13 @@ func newDocStore(ctx context.Context, cfg *ClassConfig) (*docStore, error) { } // Indexer returns the indexer for the given class. -func Indexer(class string) ai.Indexer { - return ai.LookupIndexer(provider, class) +func Indexer(g *genkit.Genkit, class string) ai.Indexer { + return genkit.LookupIndexer(g, provider, class) } // Retriever returns the retriever for the given class. -func Retriever(class string) ai.Retriever { - return ai.LookupRetriever(provider, class) +func Retriever(g *genkit.Genkit, class string) ai.Retriever { + return genkit.LookupRetriever(g, provider, class) } // Index implements the genkit Retriever.Index method. diff --git a/go/plugins/weaviate/weaviate_test.go b/go/plugins/weaviate/weaviate_test.go index a08b7374a..c1f1ad533 100644 --- a/go/plugins/weaviate/weaviate_test.go +++ b/go/plugins/weaviate/weaviate_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/internal/fakeembedder" ) @@ -41,6 +42,11 @@ func TestGenkit(t *testing.T) { ctx := context.Background() + g, err := genkit.New(nil) + if err != nil { + t.Fatal(err) + } + // Make two very similar vectors and one different vector. // Arrange for a fake embedder to return those vectors // when provided with documents. @@ -83,9 +89,9 @@ func TestGenkit(t *testing.T) { classCfg := ClassConfig{ Class: *testClass, - Embedder: ai.DefineEmbedder("fake", "embedder3", embedder.Embed), + Embedder: genkit.DefineEmbedder(g, "fake", "embedder3", embedder.Embed), } - indexer, retriever, err := DefineIndexerAndRetriever(ctx, classCfg) + indexer, retriever, err := DefineIndexerAndRetriever(ctx, g, classCfg) if err != nil { t.Fatal(err) } diff --git a/go/samples/coffee-shop/main.go b/go/samples/coffee-shop/main.go index 7950a4e3e..28e76ccf6 100755 --- a/go/samples/coffee-shop/main.go +++ b/go/samples/coffee-shop/main.go @@ -94,13 +94,20 @@ type testAllCoffeeFlowsOutput struct { } func main() { - if err := googleai.Init(context.Background(), nil); err != nil { + g, err := genkit.New(&genkit.Options{ + DefaultModel: "googleai/gemini-1.5-flash", + }) + if err != nil { + log.Fatalf("failed to create Genkit: %v", err) + } + + if err := googleai.Init(context.Background(), g, nil); err != nil { log.Fatal(err) } - g := googleai.Model("gemini-1.5-pro") - simpleGreetingPrompt, err := dotprompt.Define("simpleGreeting2", simpleGreetingPromptTemplate, - dotprompt.WithDefaultModel(g), + m := googleai.Model(g, "gemini-1.5-pro") + simpleGreetingPrompt, err := dotprompt.Define(g, "simpleGreeting2", simpleGreetingPromptTemplate, + dotprompt.WithDefaultModel(m), dotprompt.WithInputType(simpleGreetingInput{}), dotprompt.WithOutputFormat(ai.OutputFormatText), ) @@ -108,7 +115,7 @@ func main() { log.Fatal(err) } - simpleGreetingFlow := genkit.DefineStreamingFlow("simpleGreeting", func(ctx context.Context, input *simpleGreetingInput, cb func(context.Context, string) error) (string, error) { + simpleGreetingFlow := genkit.DefineStreamingFlow(g, "simpleGreeting", func(ctx context.Context, input *simpleGreetingInput, cb func(context.Context, string) error) (string, error) { var callback func(context.Context, *ai.ModelResponseChunk) error if cb != nil { callback = func(ctx context.Context, c *ai.ModelResponseChunk) error { @@ -116,6 +123,7 @@ func main() { } } resp, err := simpleGreetingPrompt.Generate(ctx, + g, dotprompt.WithInput(input), dotprompt.WithStreaming(callback), ) @@ -125,8 +133,8 @@ func main() { return resp.Text(), nil }) - greetingWithHistoryPrompt, err := dotprompt.Define("greetingWithHistory", greetingWithHistoryPromptTemplate, - dotprompt.WithDefaultModel(g), + greetingWithHistoryPrompt, err := dotprompt.Define(g, "greetingWithHistory", greetingWithHistoryPromptTemplate, + dotprompt.WithDefaultModel(m), dotprompt.WithInputType(customerTimeAndHistoryInput{}), dotprompt.WithOutputFormat(ai.OutputFormatText), ) @@ -134,8 +142,8 @@ func main() { log.Fatal(err) } - greetingWithHistoryFlow := genkit.DefineFlow("greetingWithHistory", func(ctx context.Context, input *customerTimeAndHistoryInput) (string, error) { - resp, err := greetingWithHistoryPrompt.Generate(ctx, + greetingWithHistoryFlow := genkit.DefineFlow(g, "greetingWithHistory", func(ctx context.Context, input *customerTimeAndHistoryInput) (string, error) { + resp, err := greetingWithHistoryPrompt.Generate(ctx, g, dotprompt.WithInput(input), nil, ) @@ -145,8 +153,8 @@ func main() { return resp.Text(), nil }) - simpleStructuredGreetingPrompt, err := dotprompt.Define("simpleStructuredGreeting", simpleStructuredGreetingPromptTemplate, - dotprompt.WithDefaultModel(g), + simpleStructuredGreetingPrompt, err := dotprompt.Define(g, "simpleStructuredGreeting", simpleStructuredGreetingPromptTemplate, + dotprompt.WithDefaultModel(m), dotprompt.WithInputType(simpleGreetingInput{}), dotprompt.WithOutputType(simpleGreetingOutput{}), ) @@ -154,14 +162,14 @@ func main() { log.Fatal(err) } - genkit.DefineStreamingFlow("simpleStructuredGreeting", func(ctx context.Context, input *simpleGreetingInput, cb func(context.Context, string) error) (string, error) { + genkit.DefineStreamingFlow(g, "simpleStructuredGreeting", func(ctx context.Context, input *simpleGreetingInput, cb func(context.Context, string) error) (string, error) { var callback func(context.Context, *ai.ModelResponseChunk) error if cb != nil { callback = func(ctx context.Context, c *ai.ModelResponseChunk) error { return cb(ctx, c.Text()) } } - resp, err := simpleStructuredGreetingPrompt.Generate(ctx, + resp, err := simpleStructuredGreetingPrompt.Generate(ctx, g, dotprompt.WithInput(input), dotprompt.WithStreaming(callback), ) @@ -171,7 +179,7 @@ func main() { return resp.Text(), nil }) - genkit.DefineFlow("testAllCoffeeFlows", func(ctx context.Context, _ struct{}) (*testAllCoffeeFlowsOutput, error) { + genkit.DefineFlow(g, "testAllCoffeeFlows", func(ctx context.Context, _ struct{}) (*testAllCoffeeFlowsOutput, error) { test1, err := simpleGreetingFlow.Run(ctx, &simpleGreetingInput{ CustomerName: "Sam", }) @@ -203,7 +211,8 @@ func main() { } return out, nil }) - if err := genkit.Init(context.Background(), nil); err != nil { + + if err := g.Start(context.Background(), nil); err != nil { log.Fatal(err) } } diff --git a/go/samples/firebase-auth/main.go b/go/samples/firebase-auth/main.go index 14e7bcd37..3b22dfb97 100644 --- a/go/samples/firebase-auth/main.go +++ b/go/samples/firebase-auth/main.go @@ -27,6 +27,11 @@ import ( func main() { ctx := context.Background() + g, err := genkit.New(nil) + if err != nil { + log.Fatalf("failed to create Genkit: %v", err) + } + policy := func(authContext genkit.AuthContext, input any) error { user := input.(string) if authContext == nil || authContext["UID"] != user { @@ -39,7 +44,7 @@ func main() { log.Fatalf("failed to set up Firebase auth: %v", err) } - flowWithRequiredAuth := genkit.DefineFlow("flow-with-required-auth", func(ctx context.Context, user string) (string, error) { + flowWithRequiredAuth := genkit.DefineFlow(g, "flow-with-required-auth", func(ctx context.Context, user string) (string, error) { return fmt.Sprintf("info about user %q", user), nil }, genkit.WithFlowAuth(firebaseAuth)) @@ -48,11 +53,11 @@ func main() { log.Fatalf("failed to set up Firebase auth: %v", err) } - flowWithAuth := genkit.DefineFlow("flow-with-auth", func(ctx context.Context, user string) (string, error) { + flowWithAuth := genkit.DefineFlow(g, "flow-with-auth", func(ctx context.Context, user string) (string, error) { return fmt.Sprintf("info about user %q", user), nil }, genkit.WithFlowAuth(firebaseAuth)) - genkit.DefineFlow("super-caller", func(ctx context.Context, _ struct{}) (string, error) { + genkit.DefineFlow(g, "super-caller", func(ctx context.Context, _ struct{}) (string, error) { // Auth is required so we have to pass local credentials. resp1, err := flowWithRequiredAuth.Run(ctx, "admin-user", genkit.WithLocalAuth(map[string]any{"UID": "admin-user"})) if err != nil { @@ -66,7 +71,7 @@ func main() { return resp1 + ", " + resp2, nil }) - if err := genkit.Init(ctx, nil); err != nil { + if err := g.Start(ctx, nil); err != nil { log.Fatal(err) } } diff --git a/go/samples/flow-sample1/main.go b/go/samples/flow-sample1/main.go index 68f119035..6fc848e71 100644 --- a/go/samples/flow-sample1/main.go +++ b/go/samples/flow-sample1/main.go @@ -45,7 +45,12 @@ import ( ) func main() { - basic := genkit.DefineFlow("basic", func(ctx context.Context, subject string) (string, error) { + g, err := genkit.New(nil) + if err != nil { + log.Fatalf("failed to create Genkit: %v", err) + } + + basic := genkit.DefineFlow(g, "basic", func(ctx context.Context, subject string) (string, error) { foo, err := genkit.Run(ctx, "call-llm", func() (string, error) { return "subject: " + subject, nil }) if err != nil { return "", err @@ -55,7 +60,7 @@ func main() { auth := &testAuth{} - genkit.DefineFlow("withContext", func(ctx context.Context, subject string) (string, error) { + genkit.DefineFlow(g, "withContext", func(ctx context.Context, subject string) (string, error) { authJson, err := json.Marshal(auth.FromContext(ctx)) if err != nil { return "", err @@ -64,7 +69,7 @@ func main() { return "subject=" + subject + ",auth=" + string(authJson), nil }, genkit.WithFlowAuth(auth)) - genkit.DefineFlow("parent", func(ctx context.Context, _ struct{}) (string, error) { + genkit.DefineFlow(g, "parent", func(ctx context.Context, _ struct{}) (string, error) { return basic.Run(ctx, "foo") }) @@ -73,7 +78,7 @@ func main() { Value int `json:"value"` } - genkit.DefineFlow("complex", func(ctx context.Context, c complex) (string, error) { + genkit.DefineFlow(g, "complex", func(ctx context.Context, c complex) (string, error) { foo, err := genkit.Run(ctx, "call-llm", func() (string, error) { return c.Key + ": " + strconv.Itoa(c.Value), nil }) if err != nil { return "", err @@ -81,7 +86,7 @@ func main() { return foo, nil }) - genkit.DefineFlow("throwy", func(ctx context.Context, err string) (string, error) { + genkit.DefineFlow(g, "throwy", func(ctx context.Context, err string) (string, error) { return "", errors.New(err) }) @@ -89,7 +94,7 @@ func main() { Count int `json:"count"` } - genkit.DefineStreamingFlow("streamy", func(ctx context.Context, count int, cb func(context.Context, chunk) error) (string, error) { + genkit.DefineStreamingFlow(g, "streamy", func(ctx context.Context, count int, cb func(context.Context, chunk) error) (string, error) { i := 0 if cb != nil { for ; i < count; i++ { @@ -101,7 +106,7 @@ func main() { return fmt.Sprintf("done: %d, streamed: %d times", count, i), nil }) - if err := genkit.Init(context.Background(), nil); err != nil { + if err := g.Start(context.Background(), nil); err != nil { log.Fatal(err) } } diff --git a/go/samples/menu/main.go b/go/samples/menu/main.go index b75f74823..369e93d64 100644 --- a/go/samples/menu/main.go +++ b/go/samples/menu/main.go @@ -67,21 +67,25 @@ var textMenuQuestionInputSchema = jsonschema.Reflect(textMenuQuestionInput{}) func main() { ctx := context.Background() - err := vertexai.Init(ctx, &vertexai.Config{Location: os.Getenv("GCLOUD_LOCATION")}) + g, err := genkit.New(nil) + if err != nil { + log.Fatalf("failed to create Genkit: %v", err) + } + err = vertexai.Init(ctx, g, &vertexai.Config{Location: os.Getenv("GCLOUD_LOCATION")}) if err != nil { log.Fatal(err) } - model := vertexai.Model("gemini-1.5-flash") - visionModel := vertexai.Model("gemini-1.5-flash") - embedder := vertexai.Embedder("text-embedding-004") - if err := setup01(ctx, model); err != nil { + model := vertexai.Model(g, "gemini-1.5-flash") + visionModel := vertexai.Model(g, "gemini-1.5-flash") + embedder := vertexai.Embedder(g, "text-embedding-004") + if err := setup01(g, model); err != nil { log.Fatal(err) } - if err := setup02(ctx, model); err != nil { + if err := setup02(g, model); err != nil { log.Fatal(err) } - if err := setup03(ctx, model); err != nil { + if err := setup03(g, model); err != nil { log.Fatal(err) } @@ -89,21 +93,21 @@ func main() { if err != nil { log.Fatal(err) } - indexer, retriever, err := localvec.DefineIndexerAndRetriever("go-menu_items", localvec.Config{ + indexer, retriever, err := localvec.DefineIndexerAndRetriever(g, "go-menu_items", localvec.Config{ Embedder: embedder, }) if err != nil { log.Fatal(err) } - if err := setup04(ctx, indexer, retriever, model); err != nil { + if err := setup04(g, indexer, retriever, model); err != nil { log.Fatal(err) } - if err := setup05(ctx, model, visionModel); err != nil { + if err := setup05(g, model, visionModel); err != nil { log.Fatal(err) } - if err := genkit.Init(ctx, nil); err != nil { + if err := g.Start(ctx, nil); err != nil { log.Fatal(err) } } diff --git a/go/samples/menu/s01.go b/go/samples/menu/s01.go index b9c64813e..565c3cbc7 100644 --- a/go/samples/menu/s01.go +++ b/go/samples/menu/s01.go @@ -15,25 +15,24 @@ package main import ( - "context" - "github.com/firebase/genkit/go/ai" + "github.com/firebase/genkit/go/genkit" "github.com/firebase/genkit/go/plugins/dotprompt" ) -func setup01(ctx context.Context, g ai.Model) error { - _, err := dotprompt.Define("s01_vanillaPrompt", +func setup01(g *genkit.Genkit, m ai.Model) error { + _, err := dotprompt.Define(g, "s01_vanillaPrompt", `You are acting as a helpful AI assistant named "Walt" that can answer questions about the food available on the menu at Walt's Burgers. Customer says: ${input.question}`, - dotprompt.WithDefaultModel(g), + dotprompt.WithDefaultModel(m), dotprompt.WithInputType(menuQuestionInput{}), ) if err != nil { return err } - _, err = dotprompt.Define("s01_staticMenuDotPrompt", + _, err = dotprompt.Define(g, "s01_staticMenuDotPrompt", `You are acting as a helpful AI assistant named "Walt" that can answer questions about the food available on the menu at Walt's Burgers. Here is today's menu: @@ -64,7 +63,7 @@ func setup01(ctx context.Context, g ai.Model) error { Question: {{question}} ?`, - dotprompt.WithDefaultModel(g), + dotprompt.WithDefaultModel(m), dotprompt.WithInputType(menuQuestionInput{}), dotprompt.WithOutputFormat(ai.OutputFormatText), ) diff --git a/go/samples/menu/s02.go b/go/samples/menu/s02.go index 44c73c080..7c404bf62 100644 --- a/go/samples/menu/s02.go +++ b/go/samples/menu/s02.go @@ -37,10 +37,10 @@ func menu(ctx context.Context, _ *any) ([]*menuItem, error) { return s, nil } -func setup02(_ context.Context, m ai.Model) error { - menuTool := ai.DefineTool("todaysMenu", "Use this tool to retrieve all the items on today's menu", menu) +func setup02(g *genkit.Genkit, m ai.Model) error { + menuTool := genkit.DefineTool(g, "todaysMenu", "Use this tool to retrieve all the items on today's menu", menu) - dataMenuPrompt, err := dotprompt.Define("s02_dataMenu", + dataMenuPrompt, err := dotprompt.Define(g, "s02_dataMenu", `You are acting as a helpful AI assistant named Walt that can answer questions about the food available on the menu at Walt's Burgers. @@ -59,9 +59,9 @@ func setup02(_ context.Context, m ai.Model) error { return err } - genkit.DefineFlow("s02_menuQuestion", + genkit.DefineFlow(g, "s02_menuQuestion", func(ctx context.Context, input *menuQuestionInput) (*answerOutput, error) { - resp, err := dataMenuPrompt.Generate(ctx, + resp, err := dataMenuPrompt.Generate(ctx, g, dotprompt.WithInput(input), nil, ) diff --git a/go/samples/menu/s03.go b/go/samples/menu/s03.go index 7cc1aef9f..a2ed32d7b 100644 --- a/go/samples/menu/s03.go +++ b/go/samples/menu/s03.go @@ -55,8 +55,8 @@ func (ch *chatHistoryStore) Retrieve(sessionID string) chatHistory { return ch.preamble } -func setup03(ctx context.Context, model ai.Model) error { - chatPreamblePrompt, err := dotprompt.Define("s03_chatPreamble", +func setup03(g *genkit.Genkit, m ai.Model) error { + chatPreamblePrompt, err := dotprompt.Define(g, "s03_chatPreamble", ` {{ role "user" }} Hi. What's on the menu today? @@ -71,7 +71,7 @@ func setup03(ctx context.Context, model ai.Model) error { {{this.description}} {{~/each}} Do you have any questions about the menu?`, - dotprompt.WithDefaultModel(model), + dotprompt.WithDefaultModel(m), dotprompt.WithInputType(dataMenuQuestionInput{}), dotprompt.WithOutputFormat(ai.OutputFormatText), dotprompt.WithDefaultConfig(&ai.GenerationCommonConfig{ @@ -100,7 +100,7 @@ func setup03(ctx context.Context, model ai.Model) error { sessions: make(map[string]chatHistory), } - genkit.DefineFlow("s03_multiTurnChat", + genkit.DefineFlow(g, "s03_multiTurnChat", func(ctx context.Context, input *chatSessionInput) (*chatSessionOutput, error) { history := storedHistory.Retrieve(input.SessionID) msg := &ai.Message{ @@ -110,7 +110,7 @@ func setup03(ctx context.Context, model ai.Model) error { Role: ai.RoleUser, } messages := append(slices.Clip(history), msg) - resp, err := ai.Generate(ctx, model, ai.WithMessages(messages...)) + resp, err := genkit.Generate(ctx, g, ai.WithModel(m), ai.WithMessages(messages...)) if err != nil { return nil, err } diff --git a/go/samples/menu/s04.go b/go/samples/menu/s04.go index 0be3faa81..a5c1e50b3 100644 --- a/go/samples/menu/s04.go +++ b/go/samples/menu/s04.go @@ -24,8 +24,8 @@ import ( "github.com/firebase/genkit/go/plugins/localvec" ) -func setup04(ctx context.Context, indexer ai.Indexer, retriever ai.Retriever, model ai.Model) error { - ragDataMenuPrompt, err := dotprompt.Define("s04_ragDataMenu", +func setup04(g *genkit.Genkit, indexer ai.Indexer, retriever ai.Retriever, model ai.Model) error { + ragDataMenuPrompt, err := dotprompt.Define(g, "s04_ragDataMenu", ` You are acting as Walt, a helpful AI assistant here at the restaurant. You can answer questions about the food on the menu or any other questions @@ -55,7 +55,7 @@ func setup04(ctx context.Context, indexer ai.Indexer, retriever ai.Retriever, mo Rows int `json:"rows"` } - genkit.DefineFlow("s04_indexMenuItems", + genkit.DefineFlow(g, "s04_indexMenuItems", func(ctx context.Context, input []*menuItem) (*flowOutput, error) { var docs []*ai.Document for _, m := range input { @@ -76,7 +76,7 @@ func setup04(ctx context.Context, indexer ai.Indexer, retriever ai.Retriever, mo }, ) - genkit.DefineFlow("s04_ragMenuQuestion", + genkit.DefineFlow(g, "s04_ragMenuQuestion", func(ctx context.Context, input *menuQuestionInput) (*answerOutput, error) { resp, err := ai.Retrieve(ctx, retriever, ai.WithRetrieverText(input.Question), @@ -97,7 +97,7 @@ func setup04(ctx context.Context, indexer ai.Indexer, retriever ai.Retriever, mo } presp, err := ragDataMenuPrompt.Generate( - ctx, + ctx, g, dotprompt.WithInput(questionInput), nil) if err != nil { diff --git a/go/samples/menu/s05.go b/go/samples/menu/s05.go index d561d897b..7ae82a5bd 100644 --- a/go/samples/menu/s05.go +++ b/go/samples/menu/s05.go @@ -28,8 +28,8 @@ type imageURLInput struct { ImageURL string `json:"imageUrl"` } -func setup05(ctx context.Context, gen, genVision ai.Model) error { - readMenuPrompt, err := dotprompt.Define("s05_readMenu", +func setup05(g *genkit.Genkit, gen, genVision ai.Model) error { + readMenuPrompt, err := dotprompt.Define(g, "s05_readMenu", ` Extract _all_ of the text, in order, from the following image of a restaurant menu. @@ -46,7 +46,7 @@ func setup05(ctx context.Context, gen, genVision ai.Model) error { return err } - textMenuPrompt, err := dotprompt.Define("s05_textMenu", + textMenuPrompt, err := dotprompt.Define(g, "s05_textMenu", ` You are acting as Walt, a helpful AI assistant here at the restaurant. You can answer questions about the food on the menu or any other questions @@ -73,7 +73,7 @@ func setup05(ctx context.Context, gen, genVision ai.Model) error { // and extracts all of the text from the photo of the menu. // Note that this example uses a hard-coded image file, as image input // is not currently available in the Development UI runners. - readMenuFlow := genkit.DefineFlow("s05_readMenuFlow", + readMenuFlow := genkit.DefineFlow(g, "s05_readMenuFlow", func(ctx context.Context, _ struct{}) (string, error) { image, err := os.ReadFile("testdata/menu.jpeg") if err != nil { @@ -83,7 +83,7 @@ func setup05(ctx context.Context, gen, genVision ai.Model) error { base64.StdEncoding.Encode(data, image) imageDataURL := "data:image/jpeg;base64," + string(data) - presp, err := readMenuPrompt.Generate(ctx, + presp, err := readMenuPrompt.Generate(ctx, g, dotprompt.WithInput(&imageURLInput{ ImageURL: imageDataURL, }), nil) @@ -98,10 +98,9 @@ func setup05(ctx context.Context, gen, genVision ai.Model) error { // Define a flow that generates a response to the question. // Just returns the LLM's text response to the question. - - textMenuQuestionFlow := genkit.DefineFlow("s05_textMenuQuestion", + textMenuQuestionFlow := genkit.DefineFlow(g, "s05_textMenuQuestion", func(ctx context.Context, input *textMenuQuestionInput) (*answerOutput, error) { - presp, err := textMenuPrompt.Generate(ctx, dotprompt.WithInput(input), nil) + presp, err := textMenuPrompt.Generate(ctx, g, dotprompt.WithInput(input), nil) if err != nil { return nil, err } @@ -114,7 +113,7 @@ func setup05(ctx context.Context, gen, genVision ai.Model) error { // Define a third composite flow that chains the first two flows. - genkit.DefineFlow("s05_visionMenuQuestion", + genkit.DefineFlow(g, "s05_visionMenuQuestion", func(ctx context.Context, input *menuQuestionInput) (*answerOutput, error) { menuText, err := readMenuFlow.Run(ctx, struct{}{}) if err != nil { diff --git a/go/samples/pgvector/main.go b/go/samples/pgvector/main.go index 9eb3a8302..aedee9a99 100644 --- a/go/samples/pgvector/main.go +++ b/go/samples/pgvector/main.go @@ -49,12 +49,16 @@ var ( func main() { flag.Parse() - if err := run(); err != nil { + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + if err := run(g); err != nil { log.Fatal(err) } } -func run() error { +func run(g *genkit.Genkit) error { if *connString == "" { return errors.New("need -dbconn") } @@ -62,11 +66,11 @@ func run() error { return errors.New("need -apikey") } ctx := context.Background() - if err := googleai.Init(ctx, &googleai.Config{APIKey: *apiKey}); err != nil { + if err := googleai.Init(ctx, g, &googleai.Config{APIKey: *apiKey}); err != nil { return err } const embedderName = "embedding-001" - embedder := googleai.Embedder(embedderName) + embedder := googleai.Embedder(g, embedderName) if embedder == nil { return fmt.Errorf("embedder %s is not known to the googleai plugin", embedderName) } @@ -78,21 +82,21 @@ func run() error { defer db.Close() if *index { - indexer := defineIndexer(db, embedder) + indexer := defineIndexer(g, db, embedder) if err := indexExistingRows(ctx, db, indexer); err != nil { return err } } // [START use-retr] - retriever := defineRetriever(db, embedder) + retriever := defineRetriever(g, db, embedder) type input struct { Question string Show string } - genkit.DefineFlow("askQuestion", func(ctx context.Context, in input) (string, error) { + genkit.DefineFlow(g, "askQuestion", func(ctx context.Context, in input) (string, error) { res, err := ai.Retrieve(ctx, retriever, ai.WithRetrieverOpts(in.Show), ai.WithRetrieverText(in.Question)) @@ -107,13 +111,13 @@ func run() error { }) // [END use-retr] - return genkit.Init(ctx, nil) + return g.Start(ctx, nil) } const provider = "pgvector" // [START retr] -func defineRetriever(db *sql.DB, embedder ai.Embedder) ai.Retriever { +func defineRetriever(g *genkit.Genkit, db *sql.DB, embedder ai.Embedder) ai.Retriever { f := func(ctx context.Context, req *ai.RetrieverRequest) (*ai.RetrieverResponse, error) { eres, err := ai.Embed(ctx, embedder, ai.WithEmbedDocs(req.Document)) if err != nil { @@ -153,12 +157,12 @@ func defineRetriever(db *sql.DB, embedder ai.Embedder) ai.Retriever { } return res, nil } - return ai.DefineRetriever(provider, "shows", f) + return genkit.DefineRetriever(g, provider, "shows", f) } // [END retr] -func defineIndexer(db *sql.DB, embedder ai.Embedder) ai.Indexer { +func defineIndexer(g *genkit.Genkit, db *sql.DB, embedder ai.Embedder) ai.Indexer { // The indexer assumes that each Document has a single part, to be embedded, and metadata fields // for the table primary key: show_id, season_number, episode_id. const query = ` @@ -166,7 +170,7 @@ func defineIndexer(db *sql.DB, embedder ai.Embedder) ai.Indexer { SET embedding = $4 WHERE show_id = $1 AND season_number = $2 AND episode_id = $3 ` - return ai.DefineIndexer(provider, "shows", func(ctx context.Context, req *ai.IndexerRequest) error { + return genkit.DefineIndexer(g, provider, "shows", func(ctx context.Context, req *ai.IndexerRequest) error { res, err := ai.Embed(ctx, embedder, ai.WithEmbedDocs(req.Documents...)) if err != nil { return err diff --git a/go/samples/rag/main.go b/go/samples/rag/main.go index 3983135e5..fedcda02e 100644 --- a/go/samples/rag/main.go +++ b/go/samples/rag/main.go @@ -66,21 +66,25 @@ type simpleQaPromptInput struct { } func main() { - err := googleai.Init(context.Background(), nil) + g, err := genkit.New(nil) if err != nil { log.Fatal(err) } - model := googleai.Model("gemini-1.0-pro") - embedder := googleai.Embedder("embedding-001") + err = googleai.Init(context.Background(), g, nil) + if err != nil { + log.Fatal(err) + } + model := googleai.Model(g, "gemini-1.0-pro") + embedder := googleai.Embedder(g, "embedding-001") if err := localvec.Init(); err != nil { log.Fatal(err) } - indexer, retriever, err := localvec.DefineIndexerAndRetriever("simpleQa", localvec.Config{Embedder: embedder}) + indexer, retriever, err := localvec.DefineIndexerAndRetriever(g, "simpleQa", localvec.Config{Embedder: embedder}) if err != nil { log.Fatal(err) } - simpleQaPrompt, err := dotprompt.Define("simpleQaPrompt", + simpleQaPrompt, err := dotprompt.Define(g, "simpleQaPrompt", simpleQaPromptTemplate, dotprompt.WithDefaultModel(model), dotprompt.WithInputType(simpleQaPromptInput{}), @@ -90,7 +94,7 @@ func main() { log.Fatal(err) } - genkit.DefineFlow("simpleQaFlow", func(ctx context.Context, input *simpleQaInput) (string, error) { + genkit.DefineFlow(g, "simpleQaFlow", func(ctx context.Context, input *simpleQaInput) (string, error) { d1 := ai.DocumentFromText("Paris is the capital of France", nil) d2 := ai.DocumentFromText("USA is the largest importer of coffee", nil) d3 := ai.DocumentFromText("Water exists in 3 states - solid, liquid and gas", nil) @@ -117,7 +121,7 @@ func main() { Context: sb.String(), } - resp, err := simpleQaPrompt.Generate(ctx, + resp, err := simpleQaPrompt.Generate(ctx, g, dotprompt.WithInput(promptInput), nil, ) @@ -127,7 +131,7 @@ func main() { return resp.Text(), nil }) - if err := genkit.Init(context.Background(), nil); err != nil { + if err := g.Start(context.Background(), nil); err != nil { log.Fatal(err) } } diff --git a/go/tests/test_app/main.go b/go/tests/test_app/main.go index 8c90a593c..b36dcc82e 100644 --- a/go/tests/test_app/main.go +++ b/go/tests/test_app/main.go @@ -26,16 +26,20 @@ import ( ) func main() { - model := ai.DefineModel("", "customReflector", nil, echo) - genkit.DefineFlow("testFlow", func(ctx context.Context, in string) (string, error) { - res, err := ai.Generate(ctx, model, ai.WithTextPrompt(in)) + g, err := genkit.New(nil) + if err != nil { + log.Fatal(err) + } + model := genkit.DefineModel(g, "", "customReflector", nil, echo) + genkit.DefineFlow(g, "testFlow", func(ctx context.Context, in string) (string, error) { + res, err := genkit.Generate(ctx, g, ai.WithModel(model), ai.WithTextPrompt(in)) if err != nil { return "", err } _ = res return "TBD", nil }) - if err := genkit.Init(context.Background(), nil); err != nil { + if err := g.Start(context.Background(), nil); err != nil { log.Fatal(err) } } diff --git a/js/plugins/dotprompt/src/index.ts b/js/plugins/dotprompt/src/index.ts index ee07ec62a..439f7e28c 100644 --- a/js/plugins/dotprompt/src/index.ts +++ b/js/plugins/dotprompt/src/index.ts @@ -77,7 +77,6 @@ export function loadPromptFile(registry: Registry, path: string): Dotprompt { export async function loadPromptUrl( registry: Registry, - name: string, url: string ): Promise { From b1869b78591ff10d8e5b23ffb6ea42d628a2720a Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Fri, 10 Jan 2025 13:55:56 -0500 Subject: [PATCH 175/562] refactor(js/flows): made flow be actually actions, actions can strea, moved flow server to express plugin (#1569) --- docs/auth.md | 45 +- docs/cloud-run.md | 4 +- docs/deploy-node.md | 3 +- js/core/src/action.ts | 71 +- js/core/src/flow.ts | 492 +------------- js/core/src/index.ts | 10 +- js/core/tests/action_test.ts | 29 +- js/core/tests/flow_test.ts | 121 +--- js/doc-snippets/package.json | 1 + js/doc-snippets/src/flows/express.ts | 5 +- js/doc-snippets/src/flows/index.ts | 27 +- js/genkit/src/genkit.ts | 101 +-- js/genkit/src/index.ts | 9 +- js/plugins/express/README.md | 8 +- js/plugins/express/package.json | 7 +- js/plugins/express/src/index.ts | 200 +++++- js/plugins/express/tests/express_test.ts | 217 +++++- js/plugins/firebase/package.json | 6 +- js/plugins/firebase/src/auth.ts | 5 +- js/plugins/firebase/src/functions.ts | 56 +- js/plugins/firebase/tests/functions_test.ts | 176 +++++ .../google-cloud/tests/logs_no_io_test.ts | 6 +- js/plugins/google-cloud/tests/logs_test.ts | 6 +- js/plugins/google-cloud/tests/metrics_test.ts | 24 +- js/plugins/google-cloud/tests/traces_test.ts | 12 +- js/pnpm-lock.yaml | 626 +++++++++++++++++- .../src/main/flows-firebase-functions.ts | 34 +- js/testapps/dev-ui-gallery/src/main/flows.ts | 36 +- .../dev-ui-gallery/src/main/prompts.ts | 2 +- js/testapps/docs-menu-basic/package.json | 1 + js/testapps/docs-menu-basic/src/index.ts | 3 +- js/testapps/docs-menu-rag/src/indexer.ts | 6 +- js/testapps/evals/src/pdf-rag.ts | 6 +- js/testapps/express/src/index.ts | 14 +- js/testapps/flow-sample1/src/index.ts | 51 +- js/testapps/flow-simple-ai/src/index.ts | 16 +- js/testapps/format-tester/src/index.ts | 6 +- js/testapps/langchain/src/index.ts | 10 +- js/testapps/prompt-file/src/index.ts | 2 +- js/testapps/rag/src/pdf-rag-firebase.ts | 8 +- js/testapps/rag/src/pdf-rag.ts | 14 +- js/testapps/vertexai-reranker/src/index.ts | 4 - .../src/index.ts | 4 - .../src/index.ts | 4 - .../src/index.ts | 4 - 45 files changed, 1569 insertions(+), 923 deletions(-) create mode 100644 js/plugins/firebase/tests/functions_test.ts diff --git a/docs/auth.md b/docs/auth.md index b87c19eab..000c34785 100644 --- a/docs/auth.md +++ b/docs/auth.md @@ -250,11 +250,12 @@ alongside the native flows. You have two options: 1. Use whatever server framework you like, and pass the auth context through via the flow call as noted above. -1. Use the built-in `startFlowsServer()` and provide Express middleware in the - flow config: +1. Use `startFlowsServer()` available via `@genkit-ai/express` plugin and provide + Express auth middleware in the flow server config: ```ts import { genkit, z } from 'genkit'; + import { startFlowServer, withAuth } from '@genkit-ai/express'; const ai = genkit({ ... });; @@ -263,32 +264,32 @@ alongside the native flows. You have two options: name: 'selfSummaryFlow', inputSchema: z.object({ uid: z.string() }), outputSchema: z.string(), - middleware: [ - (req, res, next) => { - const token = req.headers['authorization']; - const user = yourVerificationLibrary(token); - - // Pass auth information to the flow - req.auth = user; - next(); - } - ], - authPolicy: (auth, input) => { - if (!auth) { - throw new Error('Authorization required.'); - } - if (input.uid !== auth.uid) { - throw new Error('You may only summarize your own profile data.'); - } - } }, async (input) => { // Flow logic here... } ); - ai.startFlowServer({ - flows: [selfSummaryFlow], + const authProvider = (req, res, next) => { + const token = req.headers['authorization']; + const user = yourVerificationLibrary(token); + + // Pass auth information to the flow + req.auth = user; + next(); + }; + + startFlowServer({ + flows: [ + withAuth(selfSummaryFlow, authProvider, ({ auth, action, input, request }) => { + if (!auth) { + throw new Error('Authorization required.'); + } + if (input.uid !== auth.uid) { + throw new Error('You may only summarize your own profile data.'); + } + }) + ], }); // Registers the middleware ``` diff --git a/docs/cloud-run.md b/docs/cloud-run.md index d95c7704a..e37b655c4 100644 --- a/docs/cloud-run.md +++ b/docs/cloud-run.md @@ -55,7 +55,9 @@ endpoints. When you make the call, specify the flows you want to serve: ```ts -ai.startFlowServer({ +import { startFlowServer } from '@genkit-ai/express'; + +startFlowServer({ flows: [menuSuggestionFlow], }); ``` diff --git a/docs/deploy-node.md b/docs/deploy-node.md index 5e7609f1e..fd489345c 100644 --- a/docs/deploy-node.md +++ b/docs/deploy-node.md @@ -48,6 +48,7 @@ sample flow. ```typescript import { genkit } from 'genkit'; import { googleAI, gemini15Flash } from '@genkit-ai/googleai'; + import { startFlowServer } from '@genkit-ai/express'; const ai = genkit({ plugins: [googleAI()], @@ -66,7 +67,7 @@ sample flow. } ); - ai.startFlowServer({ + startFlowServer({ flows: [menuSuggestionFlow], }); ``` diff --git a/js/core/src/action.ts b/js/core/src/action.ts index 8c4cca4b5..a70f8eee6 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -93,6 +93,19 @@ export interface ActionFnArg { context?: any; } +/** + * Streaming response from an action. + */ +export interface StreamingResponse< + O extends z.ZodTypeAny = z.ZodTypeAny, + S extends z.ZodTypeAny = z.ZodTypeAny, +> { + /** Iterator over the streaming chunks. */ + stream: AsyncGenerator>; + /** Final output of the action. */ + output: Promise>; +} + /** * Self-describing, validating, observable, locally and remotely callable function. */ @@ -101,7 +114,7 @@ export type Action< O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny, > = (( - input: z.infer, + input?: z.infer, options?: ActionRunOptions ) => Promise>) & { __action: ActionMetadata; @@ -110,6 +123,11 @@ export type Action< input: z.infer, options?: ActionRunOptions> ): Promise>>; + + stream( + input?: z.infer, + opts?: ActionRunOptions> + ): StreamingResponse; }; /** @@ -205,6 +223,7 @@ export function actionWithMiddleware< throw new Error('unspported middleware function shape'); } }; + wrapped.stream = action.stream; return { result: await dispatch(0, req, options), telemetry }; }; @@ -230,7 +249,10 @@ export function action< typeof config.name === 'string' ? config.name : `${config.name.pluginId}/${config.name.actionId}`; - const actionFn = async (input: I, options?: ActionRunOptions>) => { + const actionFn = async ( + input?: I, + options?: ActionRunOptions> + ) => { return (await actionFn.run(input, options)).result; }; actionFn.__registry = registry; @@ -280,7 +302,7 @@ export function action< const output = await fn(input, { // Context can either be explicitly set, or inherited from the parent action. context: options?.context ?? getContext(registry), - sendChunk: options?.onChunk ?? ((c) => {}), + sendChunk: options?.onChunk ?? sentinelNoopStreamingCallback, }); metadata.output = JSON.stringify(output); @@ -306,6 +328,49 @@ export function action< }; }; + actionFn.stream = ( + input?: z.infer, + opts?: ActionRunOptions> + ): StreamingResponse => { + let chunkStreamController: ReadableStreamController>; + const chunkStream = new ReadableStream>({ + start(controller) { + chunkStreamController = controller; + }, + pull() {}, + cancel() {}, + }); + + const invocationPromise = actionFn + .run(config.inputSchema ? config.inputSchema.parse(input) : input, { + onChunk: ((chunk: z.infer) => { + chunkStreamController.enqueue(chunk); + }) as S extends z.ZodVoid ? undefined : StreamingCallback>, + context: opts?.context, + }) + .then((s) => s.result) + .finally(() => { + chunkStreamController.close(); + }); + + return { + output: invocationPromise, + stream: (async function* () { + const reader = chunkStream.getReader(); + while (true) { + const chunk = await reader.read(); + if (chunk.value) { + yield chunk.value; + } + if (chunk.done) { + break; + } + } + return await invocationPromise; + })(), + }; + }; + if (config.use) { return actionWithMiddleware(actionFn, config.use); } diff --git a/js/core/src/flow.ts b/js/core/src/flow.ts index 21b3f056a..7c416f6ec 100644 --- a/js/core/src/flow.ts +++ b/js/core/src/flow.ts @@ -14,52 +14,29 @@ * limitations under the License. */ -import * as bodyParser from 'body-parser'; -import cors, { CorsOptions } from 'cors'; -import express from 'express'; -import { Server } from 'http'; import { AsyncLocalStorage } from 'node:async_hooks'; import { z } from 'zod'; -import { - Action, - ActionResult, - defineAction, - sentinelNoopStreamingCallback, - StreamingCallback, -} from './action.js'; +import { Action, defineAction, StreamingCallback } from './action.js'; import { runWithContext } from './context.js'; -import { getErrorMessage, getErrorStack } from './error.js'; -import { logger } from './logging.js'; import { HasRegistry, Registry } from './registry.js'; import { runInNewSpan, SPAN_TYPE_ATTR } from './tracing.js'; -const streamDelimiter = '\n\n'; - /** - * Flow Auth policy. Consumes the authorization context of the flow and - * performs checks before the flow runs. If this throws, the flow will not - * be executed. + * Flow is an observable, streamable, (optionally) strongly typed function. */ -export interface FlowAuthPolicy { - (auth: any | undefined, input: z.infer): void | Promise; -} - -/** - * For express-based flows, req.auth should contain the value to bepassed into - * the flow context. - * - * @hidden - */ -export interface __RequestWithAuth extends express.Request { - auth?: unknown; -} +export interface Flow< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + S extends z.ZodTypeAny = z.ZodTypeAny, +> extends Action {} /** - * Configuration for a flow. + * Configuration for a streaming flow. */ export interface FlowConfig< I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, + S extends z.ZodTypeAny = z.ZodTypeAny, > { /** Name of the flow. */ name: string; @@ -67,78 +44,10 @@ export interface FlowConfig< inputSchema?: I; /** Schema of the output from the flow. */ outputSchema?: O; - /** Auth policy. */ - authPolicy?: FlowAuthPolicy; - /** Middleware for HTTP requests. Not called for direct invocations. */ - middleware?: express.RequestHandler[]; -} - -/** - * Configuration for a streaming flow. - */ -export interface StreamingFlowConfig< - I extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - S extends z.ZodTypeAny = z.ZodTypeAny, -> extends FlowConfig { /** Schema of the streaming chunks from the flow. */ streamSchema?: S; } -export interface FlowCallOptions { - /** @deprecated use {@link context} instead. */ - withLocalAuthContext?: unknown; - context?: unknown; - onChunk?: StreamingCallback; -} - -/** - * Non-streaming flow that can be called directly like a function. - */ -export interface CallableFlow< - I extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - S extends z.ZodTypeAny = z.ZodTypeAny, -> { - (input?: z.infer, opts?: FlowCallOptions>): Promise>; - - stream( - input?: z.infer, - opts?: FlowCallOptions> - ): StreamingResponse; - - flow: Flow; -} - -/** - * Streaming flow that can be called directly like a function. - * @deprecated use {@link CallableFlow} - */ -export interface StreamableFlow< - I extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - S extends z.ZodTypeAny = z.ZodTypeAny, -> { - ( - input?: z.infer, - opts?: FlowCallOptions> - ): StreamingResponse; - flow: Flow; -} - -/** - * Response from a streaming flow. - */ -interface StreamingResponse< - O extends z.ZodTypeAny = z.ZodTypeAny, - S extends z.ZodTypeAny = z.ZodTypeAny, -> { - /** Iterator over the streaming chunks. */ - stream: AsyncGenerator>; - /** Final output of the flow. */ - output: Promise>; -} - /** * Flow execution context for flow to access the streaming callback and * side-channel context data. The context itself is a function, a short-cut @@ -172,331 +81,6 @@ export type FlowFn< streamingCallback: FlowSideChannel> ) => Promise> | z.infer; -export class Flow< - I extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - S extends z.ZodTypeAny = z.ZodTypeAny, -> { - readonly name: string; - readonly inputSchema?: I; - readonly outputSchema?: O; - readonly streamSchema?: S; - readonly authPolicy?: FlowAuthPolicy; - readonly middleware?: express.RequestHandler[]; - readonly action: Action; - - constructor( - readonly registry: Registry, - config: FlowConfig | StreamingFlowConfig, - action: Action - ) { - this.name = config.name; - this.inputSchema = config.inputSchema; - this.outputSchema = config.outputSchema; - this.streamSchema = - 'streamSchema' in config ? config.streamSchema : undefined; - this.authPolicy = config.authPolicy; - this.middleware = config.middleware; - this.action = action; - } - - /** - * Executes the flow with the input directly. - */ - async invoke( - input: unknown, - opts: { - onChunk?: StreamingCallback>; - labels?: Record; - context?: unknown; - } - ): Promise>> { - await this.registry.initializeAllPlugins(); - return await this.action.run(input, { - context: opts.context, - telemetryLabels: opts.labels, - onChunk: opts.onChunk ?? sentinelNoopStreamingCallback, - }); - } - - /** - * Runs the flow. This is used when calling a flow from another flow. - */ - async run( - payload?: z.infer, - opts?: FlowCallOptions> - ): Promise> { - const input = this.inputSchema ? this.inputSchema.parse(payload) : payload; - await this.authPolicy?.(opts?.withLocalAuthContext, payload); - - if (this.middleware) { - logger.warn( - `Flow (${this.name}) middleware won't run when invoked with runFlow.` - ); - } - - const result = await this.invoke(input, { - context: opts?.context || opts?.withLocalAuthContext, - onChunk: opts?.onChunk, - }); - return result.result; - } - - /** - * Runs the flow and streams results. This is used when calling a flow from another flow. - */ - stream( - payload?: z.infer, - opts?: Omit>, 'onChunk'> - ): StreamingResponse { - let chunkStreamController: ReadableStreamController>; - const chunkStream = new ReadableStream>({ - start(controller) { - chunkStreamController = controller; - }, - pull() {}, - cancel() {}, - }); - - const authPromise = - this.authPolicy?.(opts?.context || opts?.withLocalAuthContext, payload) ?? - Promise.resolve(); - - const invocationPromise = authPromise - .then(() => - this.invoke( - this.inputSchema ? this.inputSchema.parse(payload) : payload, - { - onChunk: ((chunk: z.infer) => { - chunkStreamController.enqueue(chunk); - }) as S extends z.ZodVoid - ? undefined - : StreamingCallback>, - context: opts?.context || opts?.withLocalAuthContext, - } - ).then((s) => s.result) - ) - .finally(() => { - chunkStreamController.close(); - }); - - return { - output: invocationPromise, - stream: (async function* () { - const reader = chunkStream.getReader(); - while (true) { - const chunk = await reader.read(); - if (chunk.value) { - yield chunk.value; - } - if (chunk.done) { - break; - } - } - return await invocationPromise; - })(), - }; - } - - async expressHandler( - request: __RequestWithAuth, - response: express.Response - ): Promise { - const { stream } = request.query; - const auth = request.auth; - - let input = request.body.data; - - try { - await this.authPolicy?.(auth, input); - } catch (e: any) { - const respBody = { - error: { - status: 'PERMISSION_DENIED', - message: e.message || 'Permission denied to resource', - }, - }; - response.status(403).send(respBody).end(); - return; - } - - if (request.get('Accept') === 'text/event-stream' || stream === 'true') { - response.writeHead(200, { - 'Content-Type': 'text/plain', - 'Transfer-Encoding': 'chunked', - }); - try { - const result = await this.invoke(input, { - onChunk: (chunk: z.infer) => { - response.write( - 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter - ); - }, - context: auth, - }); - response.write( - 'data: ' + JSON.stringify({ result: result.result }) + streamDelimiter - ); - response.end(); - } catch (e) { - console.log(e); - response.write( - 'data: ' + - JSON.stringify({ - error: { - status: 'INTERNAL', - message: getErrorMessage(e), - details: getErrorStack(e), - }, - }) + - streamDelimiter - ); - response.end(); - } - } else { - try { - const result = await this.invoke(input, { context: auth }); - response.setHeader('x-genkit-trace-id', result.telemetry.traceId); - response.setHeader('x-genkit-span-id', result.telemetry.spanId); - // Responses for non-streaming flows are passed back with the flow result stored in a field called "result." - response - .status(200) - .send({ - result: result.result, - }) - .end(); - } catch (e) { - // Errors for non-streaming flows are passed back as standard API errors. - response - .status(500) - .send({ - error: { - status: 'INTERNAL', - message: getErrorMessage(e), - details: getErrorStack(e), - }, - }) - .end(); - } - } - } -} - -/** - * Options to configure the flow server. - */ -export interface FlowServerOptions { - /** List of flows to expose via the flow server. */ - flows: (CallableFlow | StreamableFlow)[]; - /** Port to run the server on. Defaults to env.PORT or 3400. */ - port?: number; - /** CORS options for the server. */ - cors?: CorsOptions; - /** HTTP method path prefix for the exposed flows. */ - pathPrefix?: string; - /** JSON body parser options. */ - jsonParserOptions?: bodyParser.OptionsJson; -} - -/** - * Flow server exposes registered flows as HTTP endpoints. - * - * This is for use in production environments. - * - * @hidden - */ -export class FlowServer { - /** List of all running servers needed to be cleaned up on process exit. */ - private static RUNNING_SERVERS: FlowServer[] = []; - - /** Registry instance to be used for API calls. */ - private registry: Registry; - /** Options for the flow server configured by the developer. */ - private options: FlowServerOptions; - /** Port the server is actually running on. This may differ from `options.port` if the original was occupied. Null is server is not running. */ - private port: number | null = null; - /** Express server instance. Null if server is not running. */ - private server: Server | null = null; - - constructor(registry: Registry, options: FlowServerOptions) { - this.registry = registry; - this.options = { - ...options, - }; - } - - /** - * Starts the server and adds it to the list of running servers to clean up on exit. - */ - async start() { - const server = express(); - - server.use(bodyParser.json(this.options.jsonParserOptions)); - server.use(cors(this.options.cors)); - - if (!!this.options.flows) { - logger.debug('Running flow server with flow paths:'); - const pathPrefix = this.options.pathPrefix ?? ''; - this.options.flows?.forEach((flow) => { - const flowPath = `/${pathPrefix}${flow.flow.name}`; - logger.debug(` - ${flowPath}`); - flow.flow.middleware?.forEach((middleware) => - server.post(flowPath, middleware) - ); - server.post(flowPath, (req, res) => flow.flow.expressHandler(req, res)); - }); - } else { - logger.warn('No flows registered in flow server.'); - } - this.port = - this.options?.port || - (process.env.PORT ? parseInt(process.env.PORT) : 0) || - 3400; - this.server = server.listen(this.port, () => { - logger.debug(`Flow server running on http://localhost:${this.port}`); - FlowServer.RUNNING_SERVERS.push(this); - }); - } - - /** - * Stops the server and removes it from the list of running servers to clean up on exit. - */ - async stop(): Promise { - if (!this.server) { - return; - } - return new Promise((resolve, reject) => { - this.server!.close((err) => { - if (err) { - logger.error( - `Error shutting down flow server on port ${this.port}: ${err}` - ); - reject(err); - } - const index = FlowServer.RUNNING_SERVERS.indexOf(this); - if (index > -1) { - FlowServer.RUNNING_SERVERS.splice(index, 1); - } - logger.debug( - `Flow server on port ${this.port} has successfully shut down.` - ); - this.port = null; - this.server = null; - resolve(); - }); - }); - } - - /** - * Stops all running servers. - */ - static async stopAll() { - return Promise.all( - FlowServer.RUNNING_SERVERS.map((server) => server.stop()) - ); - } -} - /** * Defines a non-streaming flow. This operates on the currently active registry. */ @@ -506,46 +90,13 @@ export function defineFlow< S extends z.ZodTypeAny = z.ZodTypeAny, >( registry: Registry, - config: StreamingFlowConfig | string, + config: FlowConfig | string, fn: FlowFn -): CallableFlow { - const resolvedConfig: StreamingFlowConfig = +): Flow { + const resolvedConfig: FlowConfig = typeof config === 'string' ? { name: config } : config; - const flowAction = defineFlowAction(registry, resolvedConfig, fn); - const flow = new Flow(registry, resolvedConfig, flowAction); - const callableFlow = async (input, opts) => { - return flow.run(input, opts); - }; - (callableFlow as CallableFlow).flow = flow; - (callableFlow as CallableFlow).stream = ( - input: z.infer, - opts: Omit>, 'onChunk'> - ): StreamingResponse => { - return flow.stream(input, opts); - }; - return callableFlow as CallableFlow; -} - -/** - * Defines a streaming flow. This operates on the currently active registry. - */ -export function defineStreamingFlow< - I extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - S extends z.ZodTypeAny = z.ZodTypeAny, ->( - registry: Registry, - config: StreamingFlowConfig, - fn: FlowFn -): StreamableFlow { - const flowAction = defineFlowAction(registry, config, fn); - const flow = new Flow(registry, config, flowAction); - const streamableFlow: StreamableFlow = (input, opts) => { - return flow.stream(input, opts); - }; - streamableFlow.flow = flow; - return streamableFlow; + return defineFlowAction(registry, resolvedConfig, fn); } /** @@ -557,9 +108,9 @@ function defineFlowAction< S extends z.ZodTypeAny = z.ZodTypeAny, >( registry: Registry, - config: StreamingFlowConfig, + config: FlowConfig, fn: FlowFn -): Action { +): Flow { return defineAction( registry, { @@ -568,12 +119,8 @@ function defineFlowAction< inputSchema: config.inputSchema, outputSchema: config.outputSchema, streamSchema: config.streamSchema, - metadata: { - requiresAuth: !!config.authPolicy, - }, }, async (input, { sendChunk, context }) => { - await config.authPolicy?.(context, input); return await legacyRegistryAls.run(registry, () => { const ctx = sendChunk; (ctx as FlowSideChannel>).sendChunk = sendChunk; @@ -661,12 +208,3 @@ export function run( } ); } - -// TODO: Verify that this works. -if (typeof module !== 'undefined' && 'hot' in module) { - (module as any).hot.accept(); - (module as any).hot.dispose(async () => { - logger.debug('Cleaning up flow server(s) before module reload...'); - await FlowServer.stopAll(); - }); -} diff --git a/js/core/src/index.ts b/js/core/src/index.ts index 7dce71659..b5b9000cc 100644 --- a/js/core/src/index.ts +++ b/js/core/src/index.ts @@ -32,19 +32,11 @@ export * from './action.js'; export { getFlowAuth } from './context.js'; export { GenkitError } from './error.js'; export { - Flow, - FlowServer, defineFlow, - defineStreamingFlow, run, - type CallableFlow, - type FlowAuthPolicy, + type Flow, type FlowConfig, type FlowFn, - type FlowServerOptions, - type StreamableFlow, - type StreamingFlowConfig, - type __RequestWithAuth, } from './flow.js'; export * from './plugin.js'; export * from './reflection.js'; diff --git a/js/core/tests/action_test.ts b/js/core/tests/action_test.ts index 30437639c..473718616 100644 --- a/js/core/tests/action_test.ts +++ b/js/core/tests/action_test.ts @@ -17,7 +17,7 @@ import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; import { z } from 'zod'; -import { action } from '../src/action.js'; +import { action, defineAction } from '../src/action.js'; import { Registry } from '../src/registry.js'; describe('action', () => { @@ -117,4 +117,31 @@ describe('action', () => { assert.deepStrictEqual(chunks, [1, 2, 3]); }); + + it('should stream the response', async () => { + const action = defineAction( + registry, + { name: 'hello', actionType: 'custom' }, + async (input, { sendChunk }) => { + sendChunk({ count: 1 }); + sendChunk({ count: 2 }); + sendChunk({ count: 3 }); + return `hi ${input}`; + } + ); + + const response = action.stream('Pavel'); + + const gotChunks: any[] = []; + for await (const chunk of response.stream) { + gotChunks.push(chunk); + } + + assert.equal(await response.output, 'hi Pavel'); + assert.deepStrictEqual(gotChunks, [ + { count: 1 }, + { count: 2 }, + { count: 3 }, + ]); + }); }); diff --git a/js/core/tests/flow_test.ts b/js/core/tests/flow_test.ts index a54423747..deb78364e 100644 --- a/js/core/tests/flow_test.ts +++ b/js/core/tests/flow_test.ts @@ -17,7 +17,7 @@ import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; -import { defineFlow, defineStreamingFlow, run } from '../src/flow.js'; +import { defineFlow, run } from '../src/flow.js'; import { defineAction, getFlowAuth, z } from '../src/index.js'; import { Registry } from '../src/registry.js'; import { enableTelemetry } from '../src/tracing.js'; @@ -42,26 +42,6 @@ function createTestFlow(registry: Registry) { ); } -function createTestStreamingFlow(registry: Registry) { - return defineStreamingFlow( - registry, - { - name: 'testFlow', - inputSchema: z.number(), - outputSchema: z.string(), - streamSchema: z.object({ count: z.number() }), - }, - async (input, streamingCallback) => { - if (streamingCallback) { - for (let i = 0; i < input; i++) { - streamingCallback({ count: i }); - } - } - return `bar ${input} ${!!streamingCallback}`; - } - ); -} - describe('flow', () => { let registry: Registry; @@ -125,52 +105,15 @@ describe('flow', () => { await assert.rejects( async () => await testFlow({ foo: 'foo', bar: 'bar' } as any), (err: Error) => { - assert.strictEqual(err.name, 'ZodError'); - assert.equal( - err.message.includes('Expected number, received string'), - true + return ( + err.name === 'Error' && + err.message.includes('Schema validation failed') ); - return true; } ); }); }); - describe('streamFlow', () => { - it('should run the flow', async () => { - const testFlow = createTestStreamingFlow(registry); - - const response = testFlow(3); - - const gotChunks: any[] = []; - for await (const chunk of response.stream) { - gotChunks.push(chunk); - } - - assert.equal(await response.output, 'bar 3 true'); - assert.deepEqual(gotChunks, [{ count: 0 }, { count: 1 }, { count: 2 }]); - }); - - it('should rethrow the error', async () => { - const testFlow = defineStreamingFlow( - registry, - { - name: 'throwing', - inputSchema: z.string(), - }, - async (input) => { - throw new Error(`stream bad happened: ${input}`); - } - ); - - const response = testFlow('foo'); - await assert.rejects(() => response.output, { - name: 'Error', - message: 'stream bad happened: foo', - }); - }); - }); - describe('getFlowAuth', () => { it('should run the flow', async () => { const testFlow = defineFlow( @@ -186,14 +129,14 @@ describe('flow', () => { ); const response = await testFlow('foo', { - withLocalAuthContext: { user: 'test-user' }, + context: { user: 'test-user' }, }); assert.equal(response, 'bar foo {"user":"test-user"}'); }); it('should streams the flow', async () => { - const testFlow = defineStreamingFlow( + const testFlow = defineFlow( registry, { name: 'testFlow', @@ -211,8 +154,8 @@ describe('flow', () => { } ); - const response = testFlow(3, { - withLocalAuthContext: { user: 'test-user' }, + const response = testFlow.stream(3, { + context: { user: 'test-user' }, }); const gotChunks: any[] = []; @@ -298,40 +241,8 @@ describe('flow', () => { assert.equal(response, 'bar foo {"user":"test-user"}'); }); - it('should streams the flow with context (old way)', async () => { - const testFlow = defineStreamingFlow( - registry, - { - name: 'testFlow', - inputSchema: z.number(), - outputSchema: z.string(), - streamSchema: z.object({ count: z.number() }), - }, - async (input, streamingCallback) => { - if (streamingCallback) { - for (let i = 0; i < input; i++) { - streamingCallback({ count: i }); - } - } - return `bar ${input} ${!!streamingCallback} ${JSON.stringify(getFlowAuth())}`; - } - ); - - const response = testFlow(3, { - context: { user: 'test-user' }, - }); - - const gotChunks: any[] = []; - for await (const chunk of response.stream) { - gotChunks.push(chunk); - } - - assert.equal(await response.output, 'bar 3 true {"user":"test-user"}'); - assert.deepEqual(gotChunks, [{ count: 0 }, { count: 1 }, { count: 2 }]); - }); - - it('should streams the flow with context (new way)', async () => { - const testFlow = defineStreamingFlow( + it('should streams the flow with context', async () => { + const testFlow = defineFlow( registry, { name: 'testFlow', @@ -347,7 +258,7 @@ describe('flow', () => { } ); - const response = testFlow(3, { + const response = testFlow.stream(3, { context: { user: 'test-user' }, }); @@ -407,9 +318,13 @@ describe('flow', () => { outputSchema: z.string(), }, async (input) => { - return run('custom', async () => { - return 'foo ' + (await testAction(undefined)); - }); + return run( + 'custom', + async () => { + return 'foo ' + (await testAction(undefined)); + }, + registry + ); } ); const result = await testFlow('foo', { context: { user: 'pavel' } }); diff --git a/js/doc-snippets/package.json b/js/doc-snippets/package.json index 16ba2d895..37e54440f 100644 --- a/js/doc-snippets/package.json +++ b/js/doc-snippets/package.json @@ -16,6 +16,7 @@ "@genkit-ai/googleai": "workspace:*", "@genkit-ai/vertexai": "workspace:*", "@genkit-ai/firebase": "workspace:*", + "@genkit-ai/express": "workspace:*", "data-urls": "^5.0.0" }, "devDependencies": { diff --git a/js/doc-snippets/src/flows/express.ts b/js/doc-snippets/src/flows/express.ts index b02e2c924..aa2ba2230 100644 --- a/js/doc-snippets/src/flows/express.ts +++ b/js/doc-snippets/src/flows/express.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { startFlowServer } from '@genkit-ai/express'; import { genkit } from 'genkit'; const ai = genkit({}); @@ -28,7 +29,7 @@ export const menuSuggestionFlow = ai.defineFlow( } ); -ai.startFlowServer({ +startFlowServer({ flows: [menuSuggestionFlow], }); // [END ex01] @@ -42,7 +43,7 @@ export const flowB = ai.defineFlow({ name: 'flowB' }, async (subject) => { // ... }); -ai.startFlowServer({ +startFlowServer({ flows: [flowB], port: 4567, cors: { diff --git a/js/doc-snippets/src/flows/index.ts b/js/doc-snippets/src/flows/index.ts index 3439da35f..a72bef3a8 100644 --- a/js/doc-snippets/src/flows/index.ts +++ b/js/doc-snippets/src/flows/index.ts @@ -15,7 +15,7 @@ */ import { gemini15Flash, googleAI } from '@genkit-ai/googleai'; -import { genkit, run, z } from 'genkit'; +import { genkit, z } from 'genkit'; const ai = genkit({ plugins: [googleAI()], @@ -84,7 +84,7 @@ export const menuSuggestionFlowMarkdown = ai.defineFlow( // [END ex03] // [START ex06] -export const menuSuggestionStreamingFlow = ai.defineStreamingFlow( +export const menuSuggestionStreamingFlow = ai.defineFlow( { name: 'menuSuggestionFlow', inputSchema: z.string(), @@ -159,22 +159,25 @@ export const menuQuestionFlow = ai.defineFlow( outputSchema: z.string(), }, async (input: string): Promise => { - const menu = await run('retrieve-daily-menu', async (): Promise => { - // Retrieve today's menu. (This could be a database access or simply - // fetching the menu from your website.) - - // [START_EXCLUDE] - const menu = ` + const menu = await ai.run( + 'retrieve-daily-menu', + async (): Promise => { + // Retrieve today's menu. (This could be a database access or simply + // fetching the menu from your website.) + + // [START_EXCLUDE] + const menu = ` Today's menu - Breakfast: spam and eggs - Lunch: spam sandwich with a cup of spam soup - Dinner: spam roast with a side of spammed potatoes `; - // [END_EXCLUDE] + // [END_EXCLUDE] - return menu; - }); + return menu; + } + ); const { text } = await ai.generate({ model: gemini15Flash, system: "Help the user answer questions about today's menu.", @@ -197,7 +200,7 @@ async function fn() { // [END ex05] // [START ex07] - const response = menuSuggestionStreamingFlow('Danube'); + const response = menuSuggestionStreamingFlow.stream('Danube'); // [END ex07] // [START ex08] for await (const chunk of response.stream) { diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index d212afdc9..b71262592 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -104,21 +104,17 @@ import { } from '@genkit-ai/ai/session'; import { resolveTools } from '@genkit-ai/ai/tool'; import { - CallableFlow, + Action, defineFlow, defineJsonSchema, defineSchema, - defineStreamingFlow, - Flow, + FlowConfig, FlowFn, - FlowServer, - FlowServerOptions, isDevEnv, JSONSchema, ReflectionServer, - StreamableFlow, + run, StreamingCallback, - StreamingFlowConfig, z, } from '@genkit-ai/core'; import { HasRegistry } from '@genkit-ai/core/registry'; @@ -182,10 +178,8 @@ export class Genkit implements HasRegistry { readonly registry: Registry; /** Reflection server for this registry. May be null if not started. */ private reflectionServer: ReflectionServer | null = null; - /** Flow server. May be null if the flow server is not enabled in configuration or not started. */ - private flowServer: FlowServer | null = null; /** List of flows that have been registered in this instance. */ - readonly flows: Flow[] = []; + readonly flows: Action[] = []; constructor(options?: GenkitOptions) { this.options = options || {}; @@ -209,33 +203,11 @@ export class Genkit implements HasRegistry { O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny, >( - config: StreamingFlowConfig | string, + config: FlowConfig | string, fn: FlowFn - ): CallableFlow { + ): Action { const flow = defineFlow(this.registry, config, fn); - this.flows.push(flow.flow); - return flow; - } - - /** - * Defines and registers a streaming flow. - * - * @deprecated use {@link defineFlow} - */ - defineStreamingFlow< - I extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - S extends z.ZodTypeAny = z.ZodTypeAny, - >( - config: StreamingFlowConfig | string, - fn: FlowFn - ): StreamableFlow { - const flow = defineStreamingFlow( - this.registry, - typeof config === 'string' ? { name: config } : config, - fn - ); - this.flows.push(flow.flow); + this.flows.push(flow); return flow; } @@ -1075,6 +1047,54 @@ export class Genkit implements HasRegistry { return currentSession as Session; } + /** + * A flow step that executes the provided function. Each run step is recorded separately in the trace. + * + * ```ts + * ai.defineFlow('hello', async() => { + * await ai.run('step1', async () => { + * // ... step 1 + * }); + * await ai.run('step2', async () => { + * // ... step 2 + * }); + * return result; + * }) + * ``` + */ + run(name: string, func: () => Promise): Promise; + + /** + * A flow step that executes the provided function. Each run step is recorded separately in the trace. + * + * ```ts + * ai.defineFlow('hello', async() => { + * await ai.run('step1', async () => { + * // ... step 1 + * }); + * await ai.run('step2', async () => { + * // ... step 2 + * }); + * return result; + * }) + */ + run( + name: string, + input: any, + func: (input?: any) => Promise + ): Promise; + + run( + name: string, + funcOrInput: () => Promise | any, + maybeFunc?: (input?: any) => Promise + ): Promise { + if (maybeFunc) { + return run(name, funcOrInput, maybeFunc, this.registry); + } + return run(name, funcOrInput, this.registry); + } + /** * Configures the Genkit instance. */ @@ -1117,9 +1137,8 @@ export class Genkit implements HasRegistry { * Stops all servers. */ async stopServers() { - await Promise.all([this.reflectionServer?.stop(), this.flowServer?.stop()]); + await this.reflectionServer?.stop(); this.reflectionServer = null; - this.flowServer = null; } private async resolveModel( @@ -1144,12 +1163,6 @@ export class Genkit implements HasRegistry { )) as ModelAction; } } - - startFlowServer(options: FlowServerOptions): FlowServer { - const flowServer = new FlowServer(this.registry, options); - flowServer.start(); - return flowServer; - } } /** @@ -1164,7 +1177,7 @@ export function genkit(options: GenkitOptions): Genkit { const shutdown = async () => { logger.info('Shutting down all Genkit servers...'); - await Promise.all([ReflectionServer.stopAll(), FlowServer.stopAll()]); + await ReflectionServer.stopAll(); process.exit(0); }; diff --git a/js/genkit/src/index.ts b/js/genkit/src/index.ts index 62f27c242..e7a7032fc 100644 --- a/js/genkit/src/index.ts +++ b/js/genkit/src/index.ts @@ -111,7 +111,6 @@ export { type SessionStore, } from '@genkit-ai/ai/session'; export { - FlowServer, GENKIT_CLIENT_HEADER, GENKIT_VERSION, GenkitError, @@ -124,28 +123,22 @@ export { getFlowAuth, getStreamingCallback, isDevEnv, - run, runWithStreamingCallback, z, type Action, type ActionMetadata, - type CallableFlow, type Flow, - type FlowAuthPolicy, type FlowConfig, type FlowFn, - type FlowServerOptions, type JSONSchema, type JSONSchema7, type Middleware, type ReflectionServerOptions, type RunActionResponse, type Status, - type StreamableFlow, type StreamingCallback, - type StreamingFlowConfig, + type StreamingResponse, type TelemetryConfig, - type __RequestWithAuth, } from '@genkit-ai/core'; export { loadPromptFile } from '@genkit-ai/dotprompt'; export { diff --git a/js/plugins/express/README.md b/js/plugins/express/README.md index 805e3d86d..8c41248e9 100644 --- a/js/plugins/express/README.md +++ b/js/plugins/express/README.md @@ -3,7 +3,7 @@ This plugin provides utilities for conveninetly exposing Genkit flows and actions via Express HTTP server as REST APIs. ```ts -import { handler } from '@genkit-ai/express'; +import { expressHandler } from '@genkit-ai/express'; import express from 'express'; const simpleFlow = ai.defineFlow( @@ -21,7 +21,7 @@ const simpleFlow = ai.defineFlow( const app = express(); app.use(express.json()); -app.post('/simpleFlow', handler(simpleFlow)); +app.post('/simpleFlow', expressHandler(simpleFlow)); app.listen(8080); ``` @@ -42,7 +42,7 @@ const authMiddleware = async (req, resp, next) => { app.post( '/simpleFlow', authMiddleware, - handler(simpleFlow, { + expressHandler(simpleFlow, { authPolicy: ({ auth }) => { if (auth.user !== 'Ali Baba') { throw new Error('not authorized'); @@ -52,7 +52,7 @@ app.post( ); ``` -Flows and actions exposed using the `handler` function can be accessed using `genkit/client` library: +Flows and actions exposed using the `expressHandler` function can be accessed using `genkit/client` library: ```ts import { runFlow, streamFlow } from 'genkit/client'; diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index 1de3220ff..aa98043b0 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -27,15 +27,20 @@ }, "author": "genkit", "license": "Apache-2.0", - "dependencies": {}, + "dependencies": { + "cors": "^2.8.5", + "body-parser": "^1.20.3" + }, "peerDependencies": { "genkit": "workspace:*", "express": "^4.21.1" }, "devDependencies": { "get-port": "^5.1.0", + "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/node": "^20.11.16", + "@types/body-parser": "^1.19.5", "npm-run-all": "^4.1.5", "rimraf": "^6.0.1", "tsup": "^8.3.5", diff --git a/js/plugins/express/src/index.ts b/js/plugins/express/src/index.ts index 0d229e8f0..cdc6ada22 100644 --- a/js/plugins/express/src/index.ts +++ b/js/plugins/express/src/index.ts @@ -14,16 +14,12 @@ * limitations under the License. */ -import express from 'express'; -import { - Action, - CallableFlow, - Flow, - runWithStreamingCallback, - z, -} from 'genkit'; +import * as bodyParser from 'body-parser'; +import cors, { CorsOptions } from 'cors'; +import express, { RequestHandler } from 'express'; +import { Action, Flow, runWithStreamingCallback, z } from 'genkit'; import { logger } from 'genkit/logging'; -import { Registry } from 'genkit/registry'; +import { Server } from 'http'; import { getErrorMessage, getErrorStack } from './utils'; const streamDelimiter = '\n\n'; @@ -36,7 +32,6 @@ export interface AuthPolicyContext< O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny, > { - flow?: Flow; action?: Action; input: z.infer; auth: any | undefined; @@ -67,23 +62,16 @@ export interface RequestWithAuth extends express.Request { /** * Exposes provided flow or an action as express handler. */ -export function handler< +export function expressHandler< I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny, >( - f: CallableFlow | Flow | Action, + action: Action, opts?: { authPolicy?: AuthPolicy; } ): express.RequestHandler { - const flow: Flow | undefined = (f as Flow).invoke - ? (f as Flow) - : (f as CallableFlow).flow - ? (f as CallableFlow).flow - : undefined; - const action: Action = flow ? flow.action : (f as Action); - const registry: Registry = flow ? flow.registry : action.__registry; return async ( request: RequestWithAuth, response: express.Response @@ -94,7 +82,6 @@ export function handler< try { await opts?.authPolicy?.({ - flow, action, auth, input, @@ -123,11 +110,14 @@ export function handler< 'data: ' + JSON.stringify({ message: chunk }) + streamDelimiter ); }; - const result = await runWithStreamingCallback(registry, onChunk, () => - action.run(input, { - onChunk, - context: auth, - }) + const result = await runWithStreamingCallback( + action.__registry, + onChunk, + () => + action.run(input, { + onChunk, + context: auth, + }) ); response.write( 'data: ' + JSON.stringify({ result: result.result }) + streamDelimiter @@ -176,3 +166,163 @@ export function handler< } }; } + +/** + * A wrapper object containing a flow with its associated auth policy. + */ +export type FlowWithAuthPolicy< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + S extends z.ZodTypeAny = z.ZodTypeAny, +> = { + flow: Flow; + authProvider: RequestHandler; + authPolicy: AuthPolicy; +}; + +/** + * Adds an auth policy to the flow. + */ +export function withAuth< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + S extends z.ZodTypeAny = z.ZodTypeAny, +>( + flow: Flow, + authProvider: RequestHandler, + authPolicy: AuthPolicy +): FlowWithAuthPolicy { + return { + flow, + authProvider, + authPolicy, + }; +} + +/** + * Options to configure the flow server. + */ +export interface FlowServerOptions { + /** List of flows to expose via the flow server. */ + flows: (Flow | FlowWithAuthPolicy)[]; + /** Port to run the server on. Defaults to env.PORT or 3400. */ + port?: number; + /** CORS options for the server. */ + cors?: CorsOptions; + /** HTTP method path prefix for the exposed flows. */ + pathPrefix?: string; + /** JSON body parser options. */ + jsonParserOptions?: bodyParser.OptionsJson; +} + +/** + * Starts an express server with the provided flows and options. + */ +export function startFlowServer(options: FlowServerOptions): FlowServer { + const server = new FlowServer(options); + server.start(); + return server; +} + +/** + * Flow server exposes registered flows as HTTP endpoints. + * + * This is for use in production environments. + * + * @hidden + */ +export class FlowServer { + /** List of all running servers needed to be cleaned up on process exit. */ + private static RUNNING_SERVERS: FlowServer[] = []; + + /** Options for the flow server configured by the developer. */ + private options: FlowServerOptions; + /** Port the server is actually running on. This may differ from `options.port` if the original was occupied. Null is server is not running. */ + private port: number | null = null; + /** Express server instance. Null if server is not running. */ + private server: Server | null = null; + + constructor(options: FlowServerOptions) { + this.options = { + ...options, + }; + } + + /** + * Starts the server and adds it to the list of running servers to clean up on exit. + */ + async start() { + const server = express(); + + server.use(bodyParser.json(this.options.jsonParserOptions)); + server.use(cors(this.options.cors)); + + logger.debug('Running flow server with flow paths:'); + const pathPrefix = this.options.pathPrefix ?? ''; + this.options.flows?.forEach((flow) => { + if ((flow as FlowWithAuthPolicy).authPolicy) { + const flowWithPolicy = flow as FlowWithAuthPolicy; + const flowPath = `/${pathPrefix}${flowWithPolicy.flow.__action.name}`; + logger.debug(` - ${flowPath}`); + server.post( + flowPath, + flowWithPolicy.authProvider, + expressHandler(flowWithPolicy.flow, { + authPolicy: flowWithPolicy.authPolicy, + }) + ); + } else { + const resolvedFlow = flow as Flow; + const flowPath = `/${pathPrefix}${resolvedFlow.__action.name}`; + logger.debug(` - ${flowPath}`); + server.post(flowPath, expressHandler(resolvedFlow)); + } + }); + this.port = + this.options?.port || + (process.env.PORT ? parseInt(process.env.PORT) : 0) || + 3400; + this.server = server.listen(this.port, () => { + logger.debug(`Flow server running on http://localhost:${this.port}`); + FlowServer.RUNNING_SERVERS.push(this); + }); + } + + /** + * Stops the server and removes it from the list of running servers to clean up on exit. + */ + async stop(): Promise { + if (!this.server) { + return; + } + return new Promise((resolve, reject) => { + this.server!.close((err) => { + if (err) { + logger.error( + `Error shutting down flow server on port ${this.port}: ${err}` + ); + reject(err); + } + const index = FlowServer.RUNNING_SERVERS.indexOf(this); + if (index > -1) { + FlowServer.RUNNING_SERVERS.splice(index, 1); + } + logger.debug( + `Flow server on port ${this.port} has successfully shut down.` + ); + this.port = null; + this.server = null; + resolve(); + }); + }); + } + + /** + * Stops all running servers. + */ + static async stopAll() { + return Promise.all( + FlowServer.RUNNING_SERVERS.map((server) => server.stop()) + ); + } +} diff --git a/js/plugins/express/tests/express_test.ts b/js/plugins/express/tests/express_test.ts index 4f5a8cf98..4866ef3f6 100644 --- a/js/plugins/express/tests/express_test.ts +++ b/js/plugins/express/tests/express_test.ts @@ -22,9 +22,15 @@ import getPort from 'get-port'; import * as http from 'http'; import assert from 'node:assert'; import { afterEach, beforeEach, describe, it } from 'node:test'; -import { RequestWithAuth, handler } from '../src/index.js'; - -describe('telemetry', async () => { +import { + FlowServer, + RequestWithAuth, + expressHandler, + startFlowServer, + withAuth, +} from '../src/index.js'; + +describe('expressHandler', async () => { let server: http.Server; let port; @@ -70,9 +76,9 @@ describe('telemetry', async () => { } ); - const flowWithContext = ai.defineFlow( + const flowWithAuth = ai.defineFlow( { - name: 'flowWithContext', + name: 'flowWithAuth', inputSchema: z.object({ question: z.string() }), }, async (input, { context }) => { @@ -84,10 +90,10 @@ describe('telemetry', async () => { app.use(express.json()); port = await getPort(); - app.post('/voidInput', handler(voidInput)); - app.post('/stringInput', handler(stringInput)); - app.post('/objectInput', handler(objectInput)); - app.post('/streamingFlow', handler(streamingFlow)); + app.post('/voidInput', expressHandler(voidInput)); + app.post('/stringInput', expressHandler(stringInput)); + app.post('/objectInput', expressHandler(objectInput)); + app.post('/streamingFlow', expressHandler(streamingFlow)); app.post( '/flowWithAuth', async (req, resp, next) => { @@ -99,7 +105,7 @@ describe('telemetry', async () => { }; next(); }, - handler(flowWithContext, { + expressHandler(flowWithAuth, { authPolicy: ({ auth, action, input, request }) => { assert.ok(auth, 'auth must be set'); assert.ok(action, 'flow must be set'); @@ -114,7 +120,7 @@ describe('telemetry', async () => { ); // Can also expose any action. - app.post('/echoModel', handler(echoModel)); + app.post('/echoModel', expressHandler(echoModel)); app.post( '/echoModelWithAuth', async (req, resp, next) => { @@ -126,7 +132,7 @@ describe('telemetry', async () => { }; next(); }, - handler(echoModel, { + expressHandler(echoModel, { authPolicy: ({ auth, action, input, request }) => { assert.ok(auth, 'auth must be set'); assert.ok(action, 'flow must be set'); @@ -324,6 +330,193 @@ describe('telemetry', async () => { }); }); +describe('startFlowServer', async () => { + let server: FlowServer; + let port; + + beforeEach(async () => { + const ai = genkit({}); + const echoModel = defineEchoModel(ai); + + const voidInput = ai.defineFlow('voidInput', async () => { + return 'banana'; + }); + + const stringInput = ai.defineFlow('stringInput', async (input) => { + const { text } = await ai.generate({ + model: 'echoModel', + prompt: input, + }); + return text; + }); + + const objectInput = ai.defineFlow( + { name: 'objectInput', inputSchema: z.object({ question: z.string() }) }, + async (input) => { + const { text } = await ai.generate({ + model: 'echoModel', + prompt: input.question, + }); + return text; + } + ); + + const streamingFlow = ai.defineFlow( + { + name: 'streamingFlow', + inputSchema: z.object({ question: z.string() }), + }, + async (input, sendChunk) => { + const { text } = await ai.generate({ + model: 'echoModel', + prompt: input.question, + onChunk: sendChunk, + }); + return text; + } + ); + + const flowWithAuth = ai.defineFlow( + { + name: 'flowWithAuth', + inputSchema: z.object({ question: z.string() }), + }, + async (input, { context }) => { + return `${input.question} - ${JSON.stringify(context)}`; + } + ); + + port = await getPort(); + + server = startFlowServer({ + flows: [ + voidInput, + stringInput, + objectInput, + streamingFlow, + withAuth( + flowWithAuth, + async (req, resp, next) => { + (req as RequestWithAuth).auth = { + user: + req.header('authorization') === 'open sesame' + ? 'Ali Baba' + : '40 thieves', + }; + return next(); + }, + ({ auth, action, input, request }) => { + assert.ok(auth, 'auth must be set'); + assert.ok(action, 'flow must be set'); + assert.ok(input, 'input must be set'); + assert.ok(request, 'request must be set'); + + if (auth.user !== 'Ali Baba') { + throw new Error('not authorized'); + } + } + ), + ], + port, + }); + }); + + afterEach(() => { + server.stop(); + }); + + describe('runFlow', () => { + it('should call a void input flow', async () => { + const result = await runFlow({ + url: `http://localhost:${port}/voidInput`, + }); + assert.strictEqual(result, 'banana'); + }); + + it('should run a flow with string input', async () => { + const result = await runFlow({ + url: `http://localhost:${port}/stringInput`, + input: 'hello', + }); + assert.strictEqual(result, 'Echo: hello'); + }); + + it('should run a flow with object input', async () => { + const result = await runFlow({ + url: `http://localhost:${port}/objectInput`, + input: { + question: 'olleh', + }, + }); + assert.strictEqual(result, 'Echo: olleh'); + }); + + it('should fail a bad input', async () => { + const result = runFlow({ + url: `http://localhost:${port}/objectInput`, + input: { + badField: 'hello', + }, + }); + await assert.rejects(result, (err: Error) => { + return err.message.includes('INVALID_ARGUMENT'); + }); + }); + + it('should call a flow with auth', async () => { + const result = await runFlow({ + url: `http://localhost:${port}/flowWithAuth`, + input: { + question: 'hello', + }, + headers: { + Authorization: 'open sesame', + }, + }); + assert.strictEqual(result, 'hello - {"user":"Ali Baba"}'); + }); + + it('should fail a flow with auth', async () => { + const result = runFlow({ + url: `http://localhost:${port}/flowWithAuth`, + input: { + question: 'hello', + }, + headers: { + Authorization: 'thieve #24', + }, + }); + await assert.rejects(result, (err) => { + return (err as Error).message.includes('not authorized'); + }); + }); + }); + + describe('streamFlow', () => { + it('stream a flow', async () => { + const result = streamFlow({ + url: `http://localhost:${port}/streamingFlow`, + input: { + question: 'olleh', + }, + }); + + const gotChunks: GenerateResponseChunkData[] = []; + for await (const chunk of result.stream()) { + gotChunks.push(chunk); + } + + assert.deepStrictEqual(gotChunks, [ + { content: [{ text: '3' }] }, + { content: [{ text: '2' }] }, + { content: [{ text: '1' }] }, + ]); + + assert.strictEqual(await result.output(), 'Echo: olleh'); + }); + }); +}); + export function defineEchoModel(ai: Genkit): ModelAction { return ai.defineModel( { diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index b3014a4ba..3a85cd3f1 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -32,6 +32,7 @@ "license": "Apache-2.0", "dependencies": { "@genkit-ai/google-cloud": "workspace:*", + "@genkit-ai/express": "workspace:*", "express": "^4.21.0", "google-auth-library": "^9.6.3" }, @@ -51,7 +52,10 @@ "@types/jest": "^29.5.12", "@jest/globals": "^29.7.0", "jest": "^29.7.0", - "ts-jest": "^29.1.2" + "ts-jest": "^29.1.2", + "express": "^4.21.1", + "get-port": "^5.1.0", + "firebase": "^11.1.0" }, "types": "./lib/index.d.ts", "exports": { diff --git a/js/plugins/firebase/src/auth.ts b/js/plugins/firebase/src/auth.ts index 18fe9819b..e9096e2a1 100644 --- a/js/plugins/firebase/src/auth.ts +++ b/js/plugins/firebase/src/auth.ts @@ -14,9 +14,10 @@ * limitations under the License. */ +import { RequestWithAuth } from '@genkit-ai/express'; import { Response } from 'express'; import { DecodedIdToken, getAuth } from 'firebase-admin/auth'; -import { __RequestWithAuth, z } from 'genkit'; +import { z } from 'genkit'; import { FunctionFlowAuth } from './functions.js'; import { initializeAppIfNecessary } from './helpers.js'; @@ -71,7 +72,7 @@ export function firebaseAuth( return; } - (req as __RequestWithAuth).auth = decoded; + (req as RequestWithAuth).auth = decoded; next(); }, diff --git a/js/plugins/firebase/src/functions.ts b/js/plugins/firebase/src/functions.ts index 89248274f..c60946a55 100644 --- a/js/plugins/firebase/src/functions.ts +++ b/js/plugins/firebase/src/functions.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { expressHandler } from '@genkit-ai/express'; import * as express from 'express'; import { getAppCheck } from 'firebase-admin/app-check'; import { @@ -21,28 +22,34 @@ import { HttpsOptions, onRequest, } from 'firebase-functions/v2/https'; -import { - CallableFlow, - Flow, - FlowAuthPolicy, - FlowFn, - Genkit, - StreamableFlow, - z, -} from 'genkit'; +import { Flow, FlowFn, Genkit, z } from 'genkit'; import { logger } from 'genkit/logging'; import { initializeAppIfNecessary } from './helpers.js'; -export type FunctionFlow< - I extends z.ZodTypeAny, - O extends z.ZodTypeAny, -> = HttpsFunction & CallableFlow; +/** + * Flow Auth policy. Consumes the authorization context of the flow and + * performs checks before the flow runs. If this throws, the flow will not + * be executed. + */ +export interface FlowAuthPolicy { + (auth: any | undefined, input: z.infer): void | Promise; +} -export type StreamingFunctionFlow< +/** + * For express-based flows, req.auth should contain the value to bepassed into + * the flow context. + * + * @hidden + */ +export interface RequestWithAuth extends express.Request { + auth?: unknown; +} + +export type FunctionFlow< I extends z.ZodTypeAny, O extends z.ZodTypeAny, S extends z.ZodTypeAny, -> = HttpsFunction & StreamableFlow; +> = HttpsFunction & { flow: Flow }; export interface FunctionFlowAuth { provider: express.RequestHandler; @@ -75,20 +82,18 @@ export function onFlow< genkit: Genkit, config: FunctionFlowConfig, steps: FlowFn -): StreamingFunctionFlow { - const f = genkit.defineStreamingFlow( +): FunctionFlow { + const flow = genkit.defineFlow( { ...config, - authPolicy: config.authPolicy.policy, }, steps - ).flow; - - const wrapped = wrapHttpsFlow(genkit, f, config); + ); - const funcFlow = wrapped as StreamingFunctionFlow; - funcFlow.flow = f; + const wrapped = wrapHttpsFlow(genkit, flow, config); + const funcFlow = wrapped as FunctionFlow; + funcFlow.flow = flow; return funcFlow; } @@ -131,7 +136,10 @@ function wrapHttpsFlow< } await config.authPolicy.provider(req, res, () => - flow.expressHandler(req, res) + expressHandler(flow, { + authPolicy: (context) => + config.authPolicy.policy(context.auth, context.input), + })(req, res, () => {}) ); } ); diff --git a/js/plugins/firebase/tests/functions_test.ts b/js/plugins/firebase/tests/functions_test.ts new file mode 100644 index 000000000..e70cf10b7 --- /dev/null +++ b/js/plugins/firebase/tests/functions_test.ts @@ -0,0 +1,176 @@ +/** + * Copyright 2024 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 { afterEach, beforeEach, describe, expect, it } from '@jest/globals'; +import express from 'express'; +import { initializeApp } from 'firebase/app'; +import { getFunctions, httpsCallableFromURL } from 'firebase/functions'; +import { Genkit, genkit } from 'genkit'; +import { runFlow, streamFlow } from 'genkit/client'; +import getPort from 'get-port'; +import * as http from 'http'; +import { RequestWithAuth, noAuth, onFlow } from '../lib/functions.js'; + +describe('function', () => { + let ai: Genkit; + let server: http.Server; + let port: number; + + beforeEach(async () => { + ai = genkit({}); + + const authPolicy = { + provider: async (req, resp, next) => { + (req as RequestWithAuth).auth = { + user: + req.header('authorization') === 'open sesame' + ? 'Ali Baba' + : '40 thieves', + }; + next(); + }, + policy: (auth, input) => { + if (auth.user !== 'Ali Baba') { + throw new Error('not authorized'); + } + }, + }; + + const flow = onFlow( + ai, + { + name: 'flow', + authPolicy: noAuth(), + }, + async (input) => { + return `hi ${input}`; + } + ); + + const streamingFlow = onFlow( + ai, + { + name: 'streamingFlow', + authPolicy: noAuth(), + }, + async (input, { sendChunk }) => { + sendChunk({ chubk: 1 }); + sendChunk({ chubk: 2 }); + sendChunk({ chubk: 3 }); + + return `hi ${input}`; + } + ); + + const flowWithAuth = onFlow( + ai, + { + name: 'flowWithAuth', + authPolicy: authPolicy, + }, + async (input, { context }) => { + return `hi ${input} - ${JSON.stringify(context)}`; + } + ); + + const streamingFlowWithAuth = onFlow( + ai, + { + name: 'streamingFlowWithAuth', + authPolicy: authPolicy, + }, + async (input, { context, sendChunk }) => { + sendChunk({ chubk: 1 }); + sendChunk({ chubk: 2 }); + sendChunk({ chubk: 3 }); + + return `hi ${input} - ${JSON.stringify(context)}`; + } + ); + const app = express(); + app.use(express.json()); + port = await getPort(); + app.post('/flow', flow); + app.post('/flowWithAuth', flowWithAuth); + app.post('/streamingFlow', streamingFlow); + app.post('/streamingFlowWithAuth', streamingFlowWithAuth); + server = app.listen(port, () => { + console.log(`Example app listening on port ${port}`); + }); + }); + + afterEach(() => { + server.close(); + }); + + it('should call as an express route using callable functions SDK', async () => { + const functions = getFunctions(initializeApp({})); + + const callableFlow = httpsCallableFromURL( + functions, + `http://localhost:${port}/flow` + ); + + const callableResponse = await callableFlow('Pavel'); + expect(callableResponse.data).toBe('hi Pavel'); + }); + + it('should stream as an express route using callable functions SDK', async () => { + const functions = getFunctions(initializeApp({})); + + const callableFlow = httpsCallableFromURL( + functions, + `http://localhost:${port}/streamingFlow` + ); + + const callableResponse = await callableFlow.stream('Pavel'); + const chunks: any[] = []; + for await (const chunk of callableResponse.stream) { + chunks.push(chunk); + } + expect(await callableResponse.data).toEqual('hi Pavel'); + expect(chunks).toStrictEqual([{ chubk: 1 }, { chubk: 2 }, { chubk: 3 }]); + }); + + it('should call as an express route using genkit client SDK', async () => { + const result = await runFlow({ + url: `http://localhost:${port}/flowWithAuth`, + input: 'Pavel', + headers: { + Authorization: 'open sesame', + }, + }); + + expect(result).toBe('hi Pavel - {"user":"Ali Baba"}'); + }); + + it('should call as an express route using genkit client SDK', async () => { + const result = await streamFlow({ + url: `http://localhost:${port}/streamingFlowWithAuth`, + input: 'Pavel', + headers: { + Authorization: 'open sesame', + }, + }); + + const chunks: any[] = []; + for await (const chunk of result.stream()) { + chunks.push(chunk); + } + + expect(await result.output()).toBe('hi Pavel - {"user":"Ali Baba"}'); + expect(chunks).toStrictEqual([{ chubk: 1 }, { chubk: 2 }, { chubk: 3 }]); + }); +}); diff --git a/js/plugins/google-cloud/tests/logs_no_io_test.ts b/js/plugins/google-cloud/tests/logs_no_io_test.ts index 8025580c1..ec00cdfec 100644 --- a/js/plugins/google-cloud/tests/logs_no_io_test.ts +++ b/js/plugins/google-cloud/tests/logs_no_io_test.ts @@ -23,7 +23,7 @@ import { jest, } from '@jest/globals'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; -import { GenerateResponseData, Genkit, genkit, run, z } from 'genkit'; +import { GenerateResponseData, Genkit, genkit, z } from 'genkit'; import assert from 'node:assert'; import { Writable } from 'stream'; import { @@ -144,8 +144,8 @@ describe('GoogleCloudLogs no I/O', () => { }; }); const testFlow = createFlowWithInput(ai, 'testFlow', async (input) => { - return await run('sub1', async () => { - return await run('sub2', async () => { + return await ai.run('sub1', async () => { + return await ai.run('sub2', async () => { return await ai.generate({ model: testModel, prompt: `${input} prompt`, diff --git a/js/plugins/google-cloud/tests/logs_test.ts b/js/plugins/google-cloud/tests/logs_test.ts index 01ffa9c0b..b2622fdf8 100644 --- a/js/plugins/google-cloud/tests/logs_test.ts +++ b/js/plugins/google-cloud/tests/logs_test.ts @@ -23,7 +23,7 @@ import { jest, } from '@jest/globals'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; -import { GenerateResponseData, Genkit, genkit, run, z } from 'genkit'; +import { GenerateResponseData, Genkit, genkit, z } from 'genkit'; import { SPAN_TYPE_ATTR, appendSpan } from 'genkit/tracing'; import assert from 'node:assert'; import { Writable } from 'stream'; @@ -147,8 +147,8 @@ describe('GoogleCloudLogs', () => { }; }); const testFlow = createFlowWithInput(ai, 'testFlow', async (input) => { - return await run('sub1', async () => { - return await run('sub2', async () => { + return await ai.run('sub1', async () => { + return await ai.run('sub2', async () => { return await ai.generate({ model: testModel, prompt: `${input} prompt`, diff --git a/js/plugins/google-cloud/tests/metrics_test.ts b/js/plugins/google-cloud/tests/metrics_test.ts index dbf232faf..190886337 100644 --- a/js/plugins/google-cloud/tests/metrics_test.ts +++ b/js/plugins/google-cloud/tests/metrics_test.ts @@ -30,7 +30,7 @@ import { SumMetricData, } from '@opentelemetry/sdk-metrics'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; -import { GenerateResponseData, Genkit, genkit, run, z } from 'genkit'; +import { GenerateResponseData, Genkit, genkit, z } from 'genkit'; import { SPAN_TYPE_ATTR, appendSpan } from 'genkit/tracing'; import assert from 'node:assert'; import { @@ -499,12 +499,12 @@ describe('GoogleCloudMetrics', () => { it('writes path metrics for a successful flow', async () => { const flow = createFlow(ai, 'pathTestFlow', async () => { - await run('step1', async () => { - return await run('substep_a', async () => { - return await run('substep_b', async () => 'res1'); + await ai.run('step1', async () => { + return await ai.run('substep_a', async () => { + return await ai.run('substep_b', async () => 'res1'); }); }); - await run('step2', async () => 'res2'); + await ai.run('step2', async () => 'res2'); return; }); @@ -544,7 +544,7 @@ describe('GoogleCloudMetrics', () => { it('writes path metrics for a failing flow with exception in root', async () => { const flow = createFlow(ai, 'testFlow', async () => { - const subPath = await run('sub-action', async () => { + const subPath = await ai.run('sub-action', async () => { return 'done'; }); return Promise.reject(new Error('failed')); @@ -582,8 +582,8 @@ describe('GoogleCloudMetrics', () => { it('writes path metrics for a failing flow with exception in subaction', async () => { const flow = createFlow(ai, 'testFlow', async () => { - const subPath1 = await run('sub-action-1', async () => { - const subPath2 = await run('sub-action-2', async () => { + const subPath1 = await ai.run('sub-action-1', async () => { + const subPath2 = await ai.run('sub-action-2', async () => { return Promise.reject(new Error('failed')); }); return 'done'; @@ -627,8 +627,8 @@ describe('GoogleCloudMetrics', () => { it('writes path metrics for a flow with exception in action', async () => { const flow = createFlow(ai, 'testFlow', async () => { - const subPath1 = await run('sub-action-1', async () => { - const subPath2 = await run('sub-action-2', async () => { + const subPath1 = await ai.run('sub-action-1', async () => { + const subPath2 = await ai.run('sub-action-2', async () => { return 'done'; }); return Promise.reject(new Error('failed')); @@ -674,10 +674,10 @@ describe('GoogleCloudMetrics', () => { it('writes path metrics for a flow with an exception in a serial action', async () => { const flow = createFlow(ai, 'testFlow', async () => { - const subPath1 = await run('sub-action-1', async () => { + const subPath1 = await ai.run('sub-action-1', async () => { return 'done'; }); - const subPath2 = await run('sub-action-2', async () => { + const subPath2 = await ai.run('sub-action-2', async () => { return Promise.reject(new Error('failed')); }); return 'done'; diff --git a/js/plugins/google-cloud/tests/traces_test.ts b/js/plugins/google-cloud/tests/traces_test.ts index fefaa4c37..e392e0875 100644 --- a/js/plugins/google-cloud/tests/traces_test.ts +++ b/js/plugins/google-cloud/tests/traces_test.ts @@ -23,7 +23,7 @@ import { jest, } from '@jest/globals'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; -import { Genkit, genkit, run, z } from 'genkit'; +import { Genkit, genkit, z } from 'genkit'; import { appendSpan } from 'genkit/tracing'; import assert from 'node:assert'; import { @@ -103,8 +103,8 @@ describe('GoogleCloudTracing', () => { it('sub actions are contained within flows', async () => { const testFlow = createFlow(ai, 'testFlow', async () => { - return await run('subAction', async () => { - return await run('subAction2', async () => { + return await ai.run('subAction', async () => { + return await ai.run('subAction2', async () => { return 'done'; }); }); @@ -137,7 +137,7 @@ describe('GoogleCloudTracing', () => { it('labels failed spans', async () => { const testFlow = createFlow(ai, 'badFlow', async () => { - return await run('badStep', async () => { + return await ai.run('badStep', async () => { throw new Error('oh no!'); }); }); @@ -159,7 +159,7 @@ describe('GoogleCloudTracing', () => { it('labels the root feature', async () => { const testFlow = createFlow(ai, 'niceFlow', async () => { - return run('niceStep', async () => {}); + return ai.run('niceStep', async () => {}); }); await testFlow(); @@ -195,7 +195,7 @@ describe('GoogleCloudTracing', () => { } ); const testFlow = createFlow(ai, 'modelFlow', async () => { - return run('runFlow', async () => { + return ai.run('runFlow', async () => { await ai.generate({ model: echoModel, prompt: 'Testing model telemetry', diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 6ee43c330..d7aab53af 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -157,6 +157,9 @@ importers: doc-snippets: dependencies: + '@genkit-ai/express': + specifier: workspace:* + version: link:../plugins/express '@genkit-ai/firebase': specifier: workspace:* version: link:../plugins/firebase @@ -401,6 +404,12 @@ importers: plugins/express: dependencies: + body-parser: + specifier: ^1.20.3 + version: 1.20.3 + cors: + specifier: ^2.8.5 + version: 2.8.5 express: specifier: ^4.21.1 version: 4.21.1 @@ -408,6 +417,12 @@ importers: specifier: workspace:* version: link:../../genkit devDependencies: + '@types/body-parser': + specifier: ^1.19.5 + version: 1.19.5 + '@types/cors': + specifier: ^2.8.17 + version: 2.8.17 '@types/express': specifier: ^4.17.21 version: 4.17.21 @@ -435,6 +450,9 @@ importers: plugins/firebase: dependencies: + '@genkit-ai/express': + specifier: workspace:* + version: link:../express '@genkit-ai/google-cloud': specifier: workspace:* version: link:../google-cloud @@ -466,6 +484,12 @@ importers: '@types/node': specifier: ^20.11.16 version: 20.11.30 + firebase: + specifier: ^11.1.0 + version: 11.1.0 + get-port: + specifier: ^5.1.0 + version: 5.1.0 jest: specifier: ^29.7.0 version: 29.7.0(@types/node@20.11.30) @@ -947,6 +971,9 @@ importers: testapps/docs-menu-basic: dependencies: + '@genkit-ai/express': + specifier: workspace:* + version: link:../../plugins/express '@genkit-ai/firebase': specifier: workspace:* version: link:../../plugins/firebase @@ -1344,7 +1371,7 @@ importers: version: link:../../plugins/ollama genkitx-openai: specifier: ^0.10.1 - version: 0.10.1(@genkit-ai/ai@1.0.0-rc.0)(@genkit-ai/core@1.0.0-rc.0) + version: 0.10.1(@genkit-ai/ai@1.0.0-rc.2)(@genkit-ai/core@1.0.0-rc.2) devDependencies: rimraf: specifier: ^6.0.1 @@ -2134,38 +2161,248 @@ packages: '@fastify/busboy@3.0.0': resolution: {integrity: sha512-83rnH2nCvclWaPQQKvkJ2pdOjG4TZyEVuFDnlOF6KP08lDaaceVyw/W63mDuafQT+MKHCvXIPpE5uYWeM0rT4w==} + '@firebase/analytics-compat@0.2.16': + resolution: {integrity: sha512-Q/s+u/TEMSb2EDJFQMGsOzpSosybBl8HuoSEMyGZ99+0Pu7SIR9MPDGUjc8PKiCFQWDJ3QXxgqh1d/rujyAMbA==} + peerDependencies: + '@firebase/app-compat': 0.x + + '@firebase/analytics-types@0.8.3': + resolution: {integrity: sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==} + + '@firebase/analytics@0.10.10': + resolution: {integrity: sha512-Psdo7c9g2SLAYh6u1XRA+RZ7ab2JfBVuAt/kLzXkhKZL/gS2cQUCMsOW5p0RIlDPRKqpdNSmvujd2TeRWLKOkQ==} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/app-check-compat@0.3.17': + resolution: {integrity: sha512-a/eadrGsY0MVCBPhrNbKUhoYpms4UKTYLKO7nswwSFVsm3Rw6NslQQCNLfvljcDqP4E7alQDRGJXjkxd/5gJ+Q==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@firebase/app-compat': 0.x + '@firebase/app-check-interop-types@0.3.1': resolution: {integrity: sha512-NILZbe6RH3X1pZmJnfOfY2gLIrlKmrkUMMrrK6VSXHcSE0eQv28xFEcw16D198i9JYZpy5Kwq394My62qCMaIw==} + '@firebase/app-check-interop-types@0.3.3': + resolution: {integrity: sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==} + + '@firebase/app-check-types@0.5.3': + resolution: {integrity: sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==} + + '@firebase/app-check@0.8.10': + resolution: {integrity: sha512-DWFfxxif/t+Ow4MmRUevDX+A3hVxm1rUf6y5ZP4sIomfnVCO1NNahqtsv9rb1/tKGkTeoVT40weiTS/WjQG1mA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/app-compat@0.2.47': + resolution: {integrity: sha512-TdEWGDp6kSwuO1mxiM2Fe39eLWygfyzqTZcoU3aPV0viqqphPCbBBnVjPbFJErZ4+yaS7uCWXEbFEP9m5/COKA==} + engines: {node: '>=18.0.0'} + '@firebase/app-types@0.9.1': resolution: {integrity: sha512-nFGqTYsnDFn1oXf1tCwPAc+hQPxyvBT/QB7qDjwK+IDYThOn63nGhzdUTXxVD9Ca8gUY/e5PQMngeo0ZW/E3uQ==} + '@firebase/app-types@0.9.3': + resolution: {integrity: sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==} + + '@firebase/app@0.10.17': + resolution: {integrity: sha512-53sIYyAnYEPIZdaxuyq5OST7j4KBc2pqmktz+tEb1BIUSbXh8Gp4k/o6qzLelLpm4ngrBz7SRN0PZJqNRAyPog==} + engines: {node: '>=18.0.0'} + + '@firebase/auth-compat@0.5.16': + resolution: {integrity: sha512-YlYwJMBqAyv0ESy3jDUyshMhZlbUiwAm6B6+uUmigNDHU+uq7j4SFiDJEZlFFIz397yBzKn06SUdqutdQzGnCA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@firebase/app-compat': 0.x + '@firebase/auth-interop-types@0.2.2': resolution: {integrity: sha512-k3NA28Jfoo0+o391bFjoV9X5QLnUL1WbLhZZRbTQhZdmdGYJfX8ixtNNlHsYQ94bwG0QRbsmvkzDnzuhHrV11w==} + '@firebase/auth-interop-types@0.2.4': + resolution: {integrity: sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==} + + '@firebase/auth-types@0.12.3': + resolution: {integrity: sha512-Zq9zI0o5hqXDtKg6yDkSnvMCMuLU6qAVS51PANQx+ZZX5xnzyNLEBO3GZgBUPsV5qIMFhjhqmLDxUqCbnAYy2A==} + peerDependencies: + '@firebase/app-types': 0.x + '@firebase/util': 1.x + + '@firebase/auth@1.8.1': + resolution: {integrity: sha512-LX9N/Cf5Z35r5yqm2+5M3+2bRRe/+RFaa/+u4HDni7TA27C/Xm4XHLKcWcLg1BzjrS4zngSaBEOSODvp6RFOqQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@firebase/app': 0.x + '@react-native-async-storage/async-storage': ^1.18.1 + peerDependenciesMeta: + '@react-native-async-storage/async-storage': + optional: true + + '@firebase/component@0.6.11': + resolution: {integrity: sha512-eQbeCgPukLgsKD0Kw5wQgsMDX5LeoI1MIrziNDjmc6XDq5ZQnuUymANQgAb2wp1tSF9zDSXyxJmIUXaKgN58Ug==} + engines: {node: '>=18.0.0'} + '@firebase/component@0.6.6': resolution: {integrity: sha512-pp7sWqHmAAlA3os6ERgoM3k5Cxff510M9RLXZ9Mc8KFKMBc2ct3RkZTWUF7ixJNvMiK/iNgRLPDrLR2gtRJ9iQ==} + '@firebase/data-connect@0.1.3': + resolution: {integrity: sha512-FbAQpWNHownJx1VTCQI4ydbWGOZmSWXoFlirQn3ItHqsLJYSywqxSgDafzvyooifFh3J/2WqaM8y9hInnPcsTw==} + peerDependencies: + '@firebase/app': 0.x + '@firebase/database-compat@1.0.4': resolution: {integrity: sha512-GEEDAvsSMAkqy0BIFSVtFzoOIIcKHFfDM4aXHtWL/JCaNn4OOjH7td73jDfN3ALvpIN4hQki0FcxQ89XjqaTjQ==} + '@firebase/database-compat@2.0.1': + resolution: {integrity: sha512-IsFivOjdE1GrjTeKoBU/ZMenESKDXidFDzZzHBPQ/4P20ptGdrl3oLlWrV/QJqJ9lND4IidE3z4Xr5JyfUW1vg==} + engines: {node: '>=18.0.0'} + '@firebase/database-types@1.0.2': resolution: {integrity: sha512-JRigr5JNLEHqOkI99tAGHDZF47469/cJz1tRAgGs8Feh+3ZmQy/vVChSqwMp2DuVUGp9PlmGsNSlpINJ/hDuIA==} + '@firebase/database-types@1.0.7': + resolution: {integrity: sha512-I7zcLfJXrM0WM+ksFmFdAMdlq/DFmpeMNa+/GNsLyFo5u/lX5zzkPzGe3srVWqaBQBY5KprylDGxOsP6ETfL0A==} + + '@firebase/database@1.0.10': + resolution: {integrity: sha512-sWp2g92u7xT4BojGbTXZ80iaSIaL6GAL0pwvM0CO/hb0nHSnABAqsH7AhnWGsGvXuEvbPr7blZylPaR9J+GSuQ==} + engines: {node: '>=18.0.0'} + '@firebase/database@1.0.4': resolution: {integrity: sha512-k84cXh+dtpzvY6yOhfyr1B+I1vjvSMtmlqotE0lTNVylc8m5nmOohjzpTLEQDrBWvwACX/VP5fEyajAdmnOKqA==} + '@firebase/firestore-compat@0.3.40': + resolution: {integrity: sha512-18HopMN811KYBc9Ptpr1Rewwio0XF09FF3jc5wtV6rGyAs815SlFFw5vW7ZeLd43zv9tlEc2FzM0H+5Vr9ZRxw==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@firebase/app-compat': 0.x + + '@firebase/firestore-types@3.0.3': + resolution: {integrity: sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==} + peerDependencies: + '@firebase/app-types': 0.x + '@firebase/util': 1.x + + '@firebase/firestore@4.7.5': + resolution: {integrity: sha512-OO3rHvjC07jL2ITN255xH/UzCVSvh6xG8oTzQdFScQvFbcm1fjCL1hgAdpDZcx3vVcKMV+6ktr8wbllkB8r+FQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/functions-compat@0.3.17': + resolution: {integrity: sha512-oj2XV8YsJYutyPCRYUfbN6swmfrL6zar0/qtqZsKT7P7btOiYRl+lD6fxtQaT+pKE5YgOBGZW//kLPZfY0jWhw==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@firebase/app-compat': 0.x + + '@firebase/functions-types@0.6.3': + resolution: {integrity: sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==} + + '@firebase/functions@0.12.0': + resolution: {integrity: sha512-plTtzY/nT0jOgHzT0vB9qch4FpHFOhCnR8HhYBqqdArG6GOQMIruKZbiTyLybO8bcaaNgQ6kSm9yohGUwxHcIw==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/installations-compat@0.2.11': + resolution: {integrity: sha512-SHRgw5LTa6v8LubmJZxcOCwEd1MfWQPUtKdiuCx2VMWnapX54skZd1PkQg0K4l3k+4ujbI2cn7FE6Li9hbChBw==} + peerDependencies: + '@firebase/app-compat': 0.x + + '@firebase/installations-types@0.5.3': + resolution: {integrity: sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==} + peerDependencies: + '@firebase/app-types': 0.x + + '@firebase/installations@0.6.11': + resolution: {integrity: sha512-w8fY8mw6fxJzsZM2ufmTtomopXl1+bn/syYon+Gpn+0p0nO1cIUEVEFrFazTLaaL9q1CaVhc3HmseRTsI3igAA==} + peerDependencies: + '@firebase/app': 0.x + '@firebase/logger@0.4.1': resolution: {integrity: sha512-tTIixB5UJbG9ZHSGZSZdX7THr3KWOLrejZ9B7jYsm6fpwgRNngKznQKA2wgYVyvBc1ta7dGFh9NtJ8n7qfiYIw==} + '@firebase/logger@0.4.4': + resolution: {integrity: sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==} + engines: {node: '>=18.0.0'} + + '@firebase/messaging-compat@0.2.15': + resolution: {integrity: sha512-mEKKASRvRWq1aBNHgioGsOYR2c5nBZpO7k90K794zjKe0WkGNf0k7PLs5SlCf8FKnzumEkhTAp/SjYxovuxa8A==} + peerDependencies: + '@firebase/app-compat': 0.x + + '@firebase/messaging-interop-types@0.2.3': + resolution: {integrity: sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==} + + '@firebase/messaging@0.12.15': + resolution: {integrity: sha512-Bz+qvWNEwEWAbYtG4An8hgcNco6NWNoNLuLbGVwPL2fAoCF1zz+dcaBp+iTR2+K199JyRyDT9yDPAXhNHNDaKQ==} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/performance-compat@0.2.11': + resolution: {integrity: sha512-DqeNBy51W2xzlklyC7Ht9JQ94HhTA08PCcM4MDeyG/ol3fqum/+YgtHWQ2IQuduqH9afETthZqLwCZiSgY7hiA==} + peerDependencies: + '@firebase/app-compat': 0.x + + '@firebase/performance-types@0.2.3': + resolution: {integrity: sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==} + + '@firebase/performance@0.6.11': + resolution: {integrity: sha512-FlkJFeqLlIeh5T4Am3uE38HVzggliDIEFy/fErEc1faINOUFCb6vQBEoNZGaXvRnTR8lh3X/hP7tv37C7BsK9g==} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/remote-config-compat@0.2.11': + resolution: {integrity: sha512-zfIjpwPrGuIOZDmduukN086qjhZ1LnbJi/iYzgua+2qeTlO0XdlE1v66gJPwygGB3TOhT0yb9EiUZ3nBNttMqg==} + peerDependencies: + '@firebase/app-compat': 0.x + + '@firebase/remote-config-types@0.3.3': + resolution: {integrity: sha512-YlRI9CHxrk3lpQuFup9N1eohpwdWayKZUNZ/YeQ0PZoncJ66P32UsKUKqVXOaieTjJIOh7yH8JEzRdht5s+d6g==} + + '@firebase/remote-config@0.4.11': + resolution: {integrity: sha512-9z0rgKuws2nj+7cdiqF+NY1QR4na6KnuOvP+jQvgilDOhGtKOcCMq5XHiu66i73A9kFhyU6QQ2pHXxcmaq1pBw==} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/storage-compat@0.3.14': + resolution: {integrity: sha512-Ok5FmXJiapaNAOQ8W8qppnfwgP8540jw2B8M0c4TFZqF4BD+CoKBxW0dRtOuLNGadLhzqqkDZZZtkexxrveQqA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@firebase/app-compat': 0.x + + '@firebase/storage-types@0.8.3': + resolution: {integrity: sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==} + peerDependencies: + '@firebase/app-types': 0.x + '@firebase/util': 1.x + + '@firebase/storage@0.13.4': + resolution: {integrity: sha512-b1KaTTRiMupFurIhpGIbReaWev0k5O3ouTHkAPcEssT+FvU3q/1JwzvkX4+ZdB60Fc43Mbp8qQ1gWfT0Z2FP9Q==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/util@1.10.2': + resolution: {integrity: sha512-qnSHIoE9FK+HYnNhTI8q14evyqbc/vHRivfB4TgCIUOl4tosmKSQlp7ltymOlMP4xVIJTg5wrkfcZ60X4nUf7Q==} + engines: {node: '>=18.0.0'} + '@firebase/util@1.9.5': resolution: {integrity: sha512-PP4pAFISDxsf70l3pEy34Mf3GkkUcVQ3MdKp6aSVb7tcpfUQxnsdV7twDd8EkfB6zZylH6wpUAoangQDmCUMqw==} - '@genkit-ai/ai@1.0.0-rc.0': - resolution: {integrity: sha512-755VyQwYXonxT9l6WShYuHPjOvhAvA0nf5hETTQtqrrE93hsG1QWVPC7tQ3lVrKNu/M4xmwGVsNgrRx3LIMYZg==} + '@firebase/vertexai@1.0.2': + resolution: {integrity: sha512-4dC9m2nD0tkfKJT5v+i27tELrmUePjFXW3CDAxhVHUEv647B2R7kqpGQnyPkNEeaXkCr76THe7GGg35EWn4lDw==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-types': 0.x + + '@firebase/webchannel-wrapper@1.0.3': + resolution: {integrity: sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==} + + '@genkit-ai/ai@1.0.0-rc.2': + resolution: {integrity: sha512-pAyjFtfw0pv9bcQHy3b40gcMUx7FL3vbdDIYvfbbizPEq+/k9DGk/HQwYI1B490xm2On/AXIvYtCo7/aTEAIcw==} - '@genkit-ai/core@1.0.0-rc.0': - resolution: {integrity: sha512-Sc/Ivlx2djH6716dfFXlBMpX6LCQRW/4rAg/AsG09Ux0Sygx8oj/GwHtRTNdzR+lNf907m21hpwxT8kLxd3a2w==} + '@genkit-ai/core@1.0.0-rc.2': + resolution: {integrity: sha512-wnrK0ZOt+VguN71kTo9/DdPchofIadKvpyG1UOzuHkc/SCMWkHzP8yHneh1clZ/uskPDR5OzrSBaQ2P0i8fwnw==} '@gerrit0/mini-shiki@1.24.4': resolution: {integrity: sha512-YEHW1QeAg6UmxEmswiQbOVEg1CW22b1XUD/lNTliOsu0LD0wqoyleFMnmbTp697QE0pcadQiR5cVtbbAPncvpw==} @@ -2272,6 +2509,10 @@ packages: resolution: {integrity: sha512-MqBisuxTkYvPFnEiu+dag3xG/NBUDzSbAFAWlzfkGnQkjVZ6by3h4atbBc+Ikqup1z5BfB4BN18gKWR1YyppNw==} engines: {node: '>=12.10.0'} + '@grpc/grpc-js@1.9.15': + resolution: {integrity: sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==} + engines: {node: ^8.13.0 || >=10.10.0} + '@grpc/proto-loader@0.7.12': resolution: {integrity: sha512-DCVwMxqYzpUCiDMl7hQ384FqP4T3DbNpXU8pt681l3UWCip1WUiD5JrkImUwCB9a7f2cq4CUTmi5r/xIMRPY1Q==} engines: {node: '>=6'} @@ -4197,6 +4438,9 @@ packages: peerDependencies: firebase-admin: ^10.0.0 || ^11.0.0 || ^12.0.0 + firebase@11.1.0: + resolution: {integrity: sha512-3OoNW3vBXmBLYJvcwbPCwfluptbDVp2zZYjrfHPVFAXfPgmyy/LWjidt+Sw2WNvRelsG0v++WN2Wor6J3OwDRg==} + flat@5.0.2: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true @@ -4483,6 +4727,9 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + idb@7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -6794,17 +7041,123 @@ snapshots: '@fastify/busboy@3.0.0': {} + '@firebase/analytics-compat@0.2.16(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17)': + dependencies: + '@firebase/analytics': 0.10.10(@firebase/app@0.10.17) + '@firebase/analytics-types': 0.8.3 + '@firebase/app-compat': 0.2.47 + '@firebase/component': 0.6.11 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/analytics-types@0.8.3': {} + + '@firebase/analytics@0.10.10(@firebase/app@0.10.17)': + dependencies: + '@firebase/app': 0.10.17 + '@firebase/component': 0.6.11 + '@firebase/installations': 0.6.11(@firebase/app@0.10.17) + '@firebase/logger': 0.4.4 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + + '@firebase/app-check-compat@0.3.17(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17)': + dependencies: + '@firebase/app-check': 0.8.10(@firebase/app@0.10.17) + '@firebase/app-check-types': 0.5.3 + '@firebase/app-compat': 0.2.47 + '@firebase/component': 0.6.11 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + '@firebase/app-check-interop-types@0.3.1': {} + '@firebase/app-check-interop-types@0.3.3': {} + + '@firebase/app-check-types@0.5.3': {} + + '@firebase/app-check@0.8.10(@firebase/app@0.10.17)': + dependencies: + '@firebase/app': 0.10.17 + '@firebase/component': 0.6.11 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + + '@firebase/app-compat@0.2.47': + dependencies: + '@firebase/app': 0.10.17 + '@firebase/component': 0.6.11 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + '@firebase/app-types@0.9.1': {} + '@firebase/app-types@0.9.3': {} + + '@firebase/app@0.10.17': + dependencies: + '@firebase/component': 0.6.11 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.10.2 + idb: 7.1.1 + tslib: 2.6.2 + + '@firebase/auth-compat@0.5.16(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17)': + dependencies: + '@firebase/app-compat': 0.2.47 + '@firebase/auth': 1.8.1(@firebase/app@0.10.17) + '@firebase/auth-types': 0.12.3(@firebase/app-types@0.9.3)(@firebase/util@1.10.2) + '@firebase/component': 0.6.11 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + - '@firebase/app-types' + - '@react-native-async-storage/async-storage' + '@firebase/auth-interop-types@0.2.2': {} + '@firebase/auth-interop-types@0.2.4': {} + + '@firebase/auth-types@0.12.3(@firebase/app-types@0.9.3)(@firebase/util@1.10.2)': + dependencies: + '@firebase/app-types': 0.9.3 + '@firebase/util': 1.10.2 + + '@firebase/auth@1.8.1(@firebase/app@0.10.17)': + dependencies: + '@firebase/app': 0.10.17 + '@firebase/component': 0.6.11 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + + '@firebase/component@0.6.11': + dependencies: + '@firebase/util': 1.10.2 + tslib: 2.6.2 + '@firebase/component@0.6.6': dependencies: '@firebase/util': 1.9.5 tslib: 2.6.2 + '@firebase/data-connect@0.1.3(@firebase/app@0.10.17)': + dependencies: + '@firebase/app': 0.10.17 + '@firebase/auth-interop-types': 0.2.4 + '@firebase/component': 0.6.11 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + '@firebase/database-compat@1.0.4': dependencies: '@firebase/component': 0.6.6 @@ -6814,11 +7167,35 @@ snapshots: '@firebase/util': 1.9.5 tslib: 2.6.2 + '@firebase/database-compat@2.0.1': + dependencies: + '@firebase/component': 0.6.11 + '@firebase/database': 1.0.10 + '@firebase/database-types': 1.0.7 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + '@firebase/database-types@1.0.2': dependencies: '@firebase/app-types': 0.9.1 '@firebase/util': 1.9.5 + '@firebase/database-types@1.0.7': + dependencies: + '@firebase/app-types': 0.9.3 + '@firebase/util': 1.10.2 + + '@firebase/database@1.0.10': + dependencies: + '@firebase/app-check-interop-types': 0.3.3 + '@firebase/auth-interop-types': 0.2.4 + '@firebase/component': 0.6.11 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.10.2 + faye-websocket: 0.11.4 + tslib: 2.6.2 + '@firebase/database@1.0.4': dependencies: '@firebase/app-check-interop-types': 0.3.1 @@ -6829,17 +7206,204 @@ snapshots: faye-websocket: 0.11.4 tslib: 2.6.2 + '@firebase/firestore-compat@0.3.40(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17)': + dependencies: + '@firebase/app-compat': 0.2.47 + '@firebase/component': 0.6.11 + '@firebase/firestore': 4.7.5(@firebase/app@0.10.17) + '@firebase/firestore-types': 3.0.3(@firebase/app-types@0.9.3)(@firebase/util@1.10.2) + '@firebase/util': 1.10.2 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + - '@firebase/app-types' + + '@firebase/firestore-types@3.0.3(@firebase/app-types@0.9.3)(@firebase/util@1.10.2)': + dependencies: + '@firebase/app-types': 0.9.3 + '@firebase/util': 1.10.2 + + '@firebase/firestore@4.7.5(@firebase/app@0.10.17)': + dependencies: + '@firebase/app': 0.10.17 + '@firebase/component': 0.6.11 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.10.2 + '@firebase/webchannel-wrapper': 1.0.3 + '@grpc/grpc-js': 1.9.15 + '@grpc/proto-loader': 0.7.13 + tslib: 2.6.2 + + '@firebase/functions-compat@0.3.17(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17)': + dependencies: + '@firebase/app-compat': 0.2.47 + '@firebase/component': 0.6.11 + '@firebase/functions': 0.12.0(@firebase/app@0.10.17) + '@firebase/functions-types': 0.6.3 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/functions-types@0.6.3': {} + + '@firebase/functions@0.12.0(@firebase/app@0.10.17)': + dependencies: + '@firebase/app': 0.10.17 + '@firebase/app-check-interop-types': 0.3.3 + '@firebase/auth-interop-types': 0.2.4 + '@firebase/component': 0.6.11 + '@firebase/messaging-interop-types': 0.2.3 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + + '@firebase/installations-compat@0.2.11(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17)': + dependencies: + '@firebase/app-compat': 0.2.47 + '@firebase/component': 0.6.11 + '@firebase/installations': 0.6.11(@firebase/app@0.10.17) + '@firebase/installations-types': 0.5.3(@firebase/app-types@0.9.3) + '@firebase/util': 1.10.2 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + - '@firebase/app-types' + + '@firebase/installations-types@0.5.3(@firebase/app-types@0.9.3)': + dependencies: + '@firebase/app-types': 0.9.3 + + '@firebase/installations@0.6.11(@firebase/app@0.10.17)': + dependencies: + '@firebase/app': 0.10.17 + '@firebase/component': 0.6.11 + '@firebase/util': 1.10.2 + idb: 7.1.1 + tslib: 2.6.2 + '@firebase/logger@0.4.1': dependencies: tslib: 2.6.2 + '@firebase/logger@0.4.4': + dependencies: + tslib: 2.6.2 + + '@firebase/messaging-compat@0.2.15(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17)': + dependencies: + '@firebase/app-compat': 0.2.47 + '@firebase/component': 0.6.11 + '@firebase/messaging': 0.12.15(@firebase/app@0.10.17) + '@firebase/util': 1.10.2 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/messaging-interop-types@0.2.3': {} + + '@firebase/messaging@0.12.15(@firebase/app@0.10.17)': + dependencies: + '@firebase/app': 0.10.17 + '@firebase/component': 0.6.11 + '@firebase/installations': 0.6.11(@firebase/app@0.10.17) + '@firebase/messaging-interop-types': 0.2.3 + '@firebase/util': 1.10.2 + idb: 7.1.1 + tslib: 2.6.2 + + '@firebase/performance-compat@0.2.11(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17)': + dependencies: + '@firebase/app-compat': 0.2.47 + '@firebase/component': 0.6.11 + '@firebase/logger': 0.4.4 + '@firebase/performance': 0.6.11(@firebase/app@0.10.17) + '@firebase/performance-types': 0.2.3 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/performance-types@0.2.3': {} + + '@firebase/performance@0.6.11(@firebase/app@0.10.17)': + dependencies: + '@firebase/app': 0.10.17 + '@firebase/component': 0.6.11 + '@firebase/installations': 0.6.11(@firebase/app@0.10.17) + '@firebase/logger': 0.4.4 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + + '@firebase/remote-config-compat@0.2.11(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17)': + dependencies: + '@firebase/app-compat': 0.2.47 + '@firebase/component': 0.6.11 + '@firebase/logger': 0.4.4 + '@firebase/remote-config': 0.4.11(@firebase/app@0.10.17) + '@firebase/remote-config-types': 0.3.3 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/remote-config-types@0.3.3': {} + + '@firebase/remote-config@0.4.11(@firebase/app@0.10.17)': + dependencies: + '@firebase/app': 0.10.17 + '@firebase/component': 0.6.11 + '@firebase/installations': 0.6.11(@firebase/app@0.10.17) + '@firebase/logger': 0.4.4 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + + '@firebase/storage-compat@0.3.14(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17)': + dependencies: + '@firebase/app-compat': 0.2.47 + '@firebase/component': 0.6.11 + '@firebase/storage': 0.13.4(@firebase/app@0.10.17) + '@firebase/storage-types': 0.8.3(@firebase/app-types@0.9.3)(@firebase/util@1.10.2) + '@firebase/util': 1.10.2 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + - '@firebase/app-types' + + '@firebase/storage-types@0.8.3(@firebase/app-types@0.9.3)(@firebase/util@1.10.2)': + dependencies: + '@firebase/app-types': 0.9.3 + '@firebase/util': 1.10.2 + + '@firebase/storage@0.13.4(@firebase/app@0.10.17)': + dependencies: + '@firebase/app': 0.10.17 + '@firebase/component': 0.6.11 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + + '@firebase/util@1.10.2': + dependencies: + tslib: 2.6.2 + '@firebase/util@1.9.5': dependencies: tslib: 2.6.2 - '@genkit-ai/ai@1.0.0-rc.0': + '@firebase/vertexai@1.0.2(@firebase/app-types@0.9.3)(@firebase/app@0.10.17)': + dependencies: + '@firebase/app': 0.10.17 + '@firebase/app-check-interop-types': 0.3.3 + '@firebase/app-types': 0.9.3 + '@firebase/component': 0.6.11 + '@firebase/logger': 0.4.4 + '@firebase/util': 1.10.2 + tslib: 2.6.2 + + '@firebase/webchannel-wrapper@1.0.3': {} + + '@genkit-ai/ai@1.0.0-rc.2': dependencies: - '@genkit-ai/core': 1.0.0-rc.0 + '@genkit-ai/core': 1.0.0-rc.2 '@opentelemetry/api': 1.9.0 '@types/node': 20.16.9 colorette: 2.0.20 @@ -6850,7 +7414,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@genkit-ai/core@1.0.0-rc.0': + '@genkit-ai/core@1.0.0-rc.2': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) @@ -7073,6 +7637,11 @@ snapshots: '@grpc/proto-loader': 0.7.12 '@js-sdsl/ordered-map': 4.4.2 + '@grpc/grpc-js@1.9.15': + dependencies: + '@grpc/proto-loader': 0.7.13 + '@types/node': 20.16.9 + '@grpc/proto-loader@0.7.12': dependencies: lodash.camelcase: 4.3.0 @@ -9240,6 +9809,39 @@ snapshots: - encoding - supports-color + firebase@11.1.0: + dependencies: + '@firebase/analytics': 0.10.10(@firebase/app@0.10.17) + '@firebase/analytics-compat': 0.2.16(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17) + '@firebase/app': 0.10.17 + '@firebase/app-check': 0.8.10(@firebase/app@0.10.17) + '@firebase/app-check-compat': 0.3.17(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17) + '@firebase/app-compat': 0.2.47 + '@firebase/app-types': 0.9.3 + '@firebase/auth': 1.8.1(@firebase/app@0.10.17) + '@firebase/auth-compat': 0.5.16(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17) + '@firebase/data-connect': 0.1.3(@firebase/app@0.10.17) + '@firebase/database': 1.0.10 + '@firebase/database-compat': 2.0.1 + '@firebase/firestore': 4.7.5(@firebase/app@0.10.17) + '@firebase/firestore-compat': 0.3.40(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17) + '@firebase/functions': 0.12.0(@firebase/app@0.10.17) + '@firebase/functions-compat': 0.3.17(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17) + '@firebase/installations': 0.6.11(@firebase/app@0.10.17) + '@firebase/installations-compat': 0.2.11(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17) + '@firebase/messaging': 0.12.15(@firebase/app@0.10.17) + '@firebase/messaging-compat': 0.2.15(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17) + '@firebase/performance': 0.6.11(@firebase/app@0.10.17) + '@firebase/performance-compat': 0.2.11(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17) + '@firebase/remote-config': 0.4.11(@firebase/app@0.10.17) + '@firebase/remote-config-compat': 0.2.11(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17) + '@firebase/storage': 0.13.4(@firebase/app@0.10.17) + '@firebase/storage-compat': 0.3.14(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17) + '@firebase/util': 1.10.2 + '@firebase/vertexai': 1.0.2(@firebase/app-types@0.9.3)(@firebase/app@0.10.17) + transitivePeerDependencies: + - '@react-native-async-storage/async-storage' + flat@5.0.2: {} fn.name@1.1.0: {} @@ -9343,10 +9945,10 @@ snapshots: - encoding - supports-color - genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-rc.0)(@genkit-ai/core@1.0.0-rc.0): + genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-rc.2)(@genkit-ai/core@1.0.0-rc.2): dependencies: - '@genkit-ai/ai': 1.0.0-rc.0 - '@genkit-ai/core': 1.0.0-rc.0 + '@genkit-ai/ai': 1.0.0-rc.2 + '@genkit-ai/core': 1.0.0-rc.2 openai: 4.53.0(encoding@0.1.13) zod: 3.23.8 transitivePeerDependencies: @@ -9665,6 +10267,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + idb@7.1.1: {} + ieee754@1.2.1: optional: true diff --git a/js/testapps/dev-ui-gallery/src/main/flows-firebase-functions.ts b/js/testapps/dev-ui-gallery/src/main/flows-firebase-functions.ts index 0558dc6d3..06ad608f6 100644 --- a/js/testapps/dev-ui-gallery/src/main/flows-firebase-functions.ts +++ b/js/testapps/dev-ui-gallery/src/main/flows-firebase-functions.ts @@ -18,37 +18,9 @@ import { firebaseAuth } from '@genkit-ai/firebase/auth'; import { noAuth, onFlow } from '@genkit-ai/firebase/functions'; import { gemini15Flash } from '@genkit-ai/googleai'; import { DecodedIdToken } from 'firebase-admin/auth'; -import { run, z } from 'genkit'; +import { z } from 'genkit'; import { ai } from '../genkit.js'; -export const flowBasicAuth = ai.defineFlow( - { - name: 'flowBasicAuth', - inputSchema: z.object({ language: z.string(), uid: z.string() }), - outputSchema: z.string(), - authPolicy: (auth, input) => { - if (!auth) { - throw new Error('Authorization required.'); - } - if (input.uid !== auth.uid) { - throw new Error('You may only summarize your own profile data.'); - } - }, - }, - async (input) => { - const prompt = `Say hello in language ${input.language}`; - - return await run('call-llm', async () => { - const llmResponse = await ai.generate({ - model: gemini15Flash, - prompt: prompt, - }); - - return llmResponse.text; - }); - } -); - export const flowAuth = onFlow( ai, { @@ -67,7 +39,7 @@ export const flowAuth = onFlow( async (language) => { const prompt = `Say hello in language ${language}`; - return await run('call-llm', async () => { + return await ai.run('call-llm', async () => { const llmResponse = await ai.generate({ model: gemini15Flash, prompt: prompt, @@ -92,7 +64,7 @@ export const flowAuthNone = onFlow( async (language) => { const prompt = `Say hello in language ${language}`; - return await run('call-llm', async () => { + return await ai.run('call-llm', async () => { const llmResponse = await ai.generate({ model: gemini15Flash, prompt: prompt, diff --git a/js/testapps/dev-ui-gallery/src/main/flows.ts b/js/testapps/dev-ui-gallery/src/main/flows.ts index ee17f0362..fa9bc346c 100644 --- a/js/testapps/dev-ui-gallery/src/main/flows.ts +++ b/js/testapps/dev-ui-gallery/src/main/flows.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { run, z } from 'genkit'; +import { z } from 'genkit'; import { generateString } from '../common/util'; import { ai } from '../genkit.js'; @@ -25,7 +25,7 @@ import { ai } from '../genkit.js'; const flowSingleStep = ai.defineFlow( { name: 'flowSingleStep' }, async (input) => { - return await run('step1', async () => { + return await ai.run('step1', async () => { return input; }); } @@ -40,15 +40,15 @@ const flowMultiStep = ai.defineFlow( async (input) => { let i = 1; - const result1 = await run('step1', async () => { + const result1 = await ai.run('step1', async () => { return `${input} ${i++},`; }); - const result2 = await run('step2', async () => { + const result2 = await ai.run('step2', async () => { return `${result1} ${i++},`; }); - return await run('step3', async () => { + return await ai.run('step3', async () => { return `${result2} ${i++}`; }); } @@ -76,7 +76,7 @@ const flowNested = ai.defineFlow( // Flow - streaming // -ai.defineStreamingFlow( +ai.defineFlow( { name: 'flowStreaming', inputSchema: z.number(), @@ -100,7 +100,7 @@ ai.defineStreamingFlow( // ai.defineFlow({ name: 'flowSingleStepThrows' }, async (input) => { - return await run('step1', async () => { + return await ai.run('step1', async () => { if (input) { throw new Error('Got an error!'); } @@ -115,18 +115,18 @@ ai.defineFlow({ name: 'flowSingleStepThrows' }, async (input) => { ai.defineFlow({ name: 'flowMultiStepThrows' }, async (input) => { let i = 1; - const result1 = await run('step1', async () => { + const result1 = await ai.run('step1', async () => { return `${input} ${i++},`; }); - const result2 = await run('step2', async () => { + const result2 = await ai.run('step2', async () => { if (result1) { throw new Error('Got an error!'); } return `${result1} ${i++},`; }); - return await run('step3', async () => { + return await ai.run('step3', async () => { return `${result2} ${i++}`; }); }); @@ -138,13 +138,13 @@ ai.defineFlow({ name: 'flowMultiStepThrows' }, async (input) => { ai.defineFlow({ name: 'flowMultiStepCaughtError' }, async (input) => { let i = 1; - const result1 = await run('step1', async () => { + const result1 = await ai.run('step1', async () => { return `${input} ${i++},`; }); let result2 = ''; try { - result2 = await run('step2', async () => { + result2 = await ai.run('step2', async () => { if (result1) { throw new Error('Got an error!'); } @@ -152,7 +152,7 @@ ai.defineFlow({ name: 'flowMultiStepCaughtError' }, async (input) => { }); } catch (e) {} - return await run('step3', async () => { + return await ai.run('step3', async () => { return `${result2} ${i++}`; }); }); @@ -161,7 +161,7 @@ ai.defineFlow({ name: 'flowMultiStepCaughtError' }, async (input) => { // Flow - streamingThrows // -ai.defineStreamingFlow( +ai.defineFlow( { name: 'flowStreamingThrows', inputSchema: z.number(), @@ -193,16 +193,16 @@ ai.defineStreamingFlow( export const largeSteps = ai.defineFlow( { name: 'flowLargeOutput' }, async () => { - await run('step1', async () => { + await ai.run('step1', async () => { return generateString(100_000); }); - await run('step2', async () => { + await ai.run('step2', async () => { return generateString(800_000); }); - await run('step3', async () => { + await ai.run('step3', async () => { return generateString(900_000); }); - await run('step4', async () => { + await ai.run('step4', async () => { return generateString(999_000); }); return 'something...'; diff --git a/js/testapps/dev-ui-gallery/src/main/prompts.ts b/js/testapps/dev-ui-gallery/src/main/prompts.ts index f9261405c..6c50672fb 100644 --- a/js/testapps/dev-ui-gallery/src/main/prompts.ts +++ b/js/testapps/dev-ui-gallery/src/main/prompts.ts @@ -89,7 +89,7 @@ export const codeDefinedPromptVariant = ai.definePrompt( template ); -ai.defineStreamingFlow( +ai.defineFlow( { name: 'flowCodeDefinedPrompt', inputSchema: HelloSchema, diff --git a/js/testapps/docs-menu-basic/package.json b/js/testapps/docs-menu-basic/package.json index fca9fd722..f3827c5f5 100644 --- a/js/testapps/docs-menu-basic/package.json +++ b/js/testapps/docs-menu-basic/package.json @@ -18,6 +18,7 @@ "genkit": "workspace:*", "@genkit-ai/firebase": "workspace:*", "@genkit-ai/googleai": "workspace:*", + "@genkit-ai/express": "workspace:*", "express": "^4.21.0" }, "devDependencies": { diff --git a/js/testapps/docs-menu-basic/src/index.ts b/js/testapps/docs-menu-basic/src/index.ts index 68229e298..034d75166 100644 --- a/js/testapps/docs-menu-basic/src/index.ts +++ b/js/testapps/docs-menu-basic/src/index.ts @@ -16,6 +16,7 @@ // This sample is referenced by the genkit docs. Changes should be made to // both. +import { startFlowServer } from '@genkit-ai/express'; import { gemini15Flash, googleAI } from '@genkit-ai/googleai'; import { genkit, z } from 'genkit'; @@ -42,6 +43,6 @@ export const menuSuggestionFlow = ai.defineFlow( } ); -ai.startFlowServer({ +startFlowServer({ flows: [menuSuggestionFlow], }); diff --git a/js/testapps/docs-menu-rag/src/indexer.ts b/js/testapps/docs-menu-rag/src/indexer.ts index be25724ee..725c88dea 100644 --- a/js/testapps/docs-menu-rag/src/indexer.ts +++ b/js/testapps/docs-menu-rag/src/indexer.ts @@ -16,7 +16,7 @@ import { devLocalIndexerRef } from '@genkit-ai/dev-local-vectorstore'; import { readFile } from 'fs/promises'; -import { run, z } from 'genkit'; +import { z } from 'genkit'; import { Document } from 'genkit/retriever'; import { chunk } from 'llm-chunk'; import path from 'path'; @@ -48,12 +48,12 @@ export const indexMenu = ai.defineFlow( filePath = path.resolve(filePath); // Read the pdf. - const pdfTxt = await run('extract-text', () => + const pdfTxt = await ai.run('extract-text', () => extractTextFromPdf(filePath) ); // Divide the pdf text into segments. - const chunks = await run('chunk-it', async () => + const chunks = await ai.run('chunk-it', async () => chunk(pdfTxt, chunkingConfig) ); diff --git a/js/testapps/evals/src/pdf-rag.ts b/js/testapps/evals/src/pdf-rag.ts index e21f4427a..813c1fd07 100644 --- a/js/testapps/evals/src/pdf-rag.ts +++ b/js/testapps/evals/src/pdf-rag.ts @@ -19,7 +19,7 @@ import { devLocalRetrieverRef, } from '@genkit-ai/dev-local-vectorstore'; import { gemini15Flash } from '@genkit-ai/googleai'; -import { run, z } from 'genkit'; +import { z } from 'genkit'; import { Document } from 'genkit/retriever'; import { chunk } from 'llm-chunk'; import path from 'path'; @@ -104,9 +104,9 @@ export const indexPdf = ai.defineFlow( }, async (filePath) => { filePath = path.resolve(filePath); - const pdfTxt = await run('extract-text', () => extractText(filePath)); + const pdfTxt = await ai.run('extract-text', () => extractText(filePath)); - const chunks = await run('chunk-it', async () => + const chunks = await ai.run('chunk-it', async () => chunk(pdfTxt, chunkingConfig) ); diff --git a/js/testapps/express/src/index.ts b/js/testapps/express/src/index.ts index 3d06255f5..16a3757ed 100644 --- a/js/testapps/express/src/index.ts +++ b/js/testapps/express/src/index.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { AuthPolicy, RequestWithAuth, handler } from '@genkit-ai/express'; +import { + AuthPolicy, + RequestWithAuth, + expressHandler, +} from '@genkit-ai/express'; import { gemini15Flash, googleAI } from '@genkit-ai/googleai'; import { vertexAI } from '@genkit-ai/vertexai'; import express, { @@ -23,7 +27,7 @@ import express, { Request, Response, } from 'express'; -import { genkit, run, z } from 'genkit'; +import { genkit, z } from 'genkit'; import { logger } from 'genkit/logging'; import { ollama } from 'genkitx-ollama'; @@ -46,7 +50,7 @@ const ai = genkit({ export const jokeFlow = ai.defineFlow( { name: 'jokeFlow', inputSchema: z.string(), outputSchema: z.string() }, async (subject, streamingCallback) => { - return await run('call-llm', async () => { + return await ai.run('call-llm', async () => { const llmResponse = await ai.generate({ prompt: `tell me long joke about ${subject}`, model: gemini15Flash, @@ -86,12 +90,12 @@ ai.flows.forEach((f) => { app.post( `/${f.name}`, auth, - handler(f, { authPolicy: authPolicies[f.name] }) + expressHandler(f, { authPolicy: authPolicies[f.name] }) ); }); // curl http://localhost:5000/jokeHandler?stream=true -d '{"data": "banana"}' -H "content-type: application/json" -app.post('/jokeHandler', handler(jokeFlow)); +app.post('/jokeHandler', expressHandler(jokeFlow)); // curl http://localhost:5000/jokeWithFlow?subject=banana app.get('/jokeWithFlow', async (req: Request, res: Response) => { diff --git a/js/testapps/flow-sample1/src/index.ts b/js/testapps/flow-sample1/src/index.ts index fcaeb84a4..d75927e32 100644 --- a/js/testapps/flow-sample1/src/index.ts +++ b/js/testapps/flow-sample1/src/index.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { genkit, getFlowAuth, run, z } from 'genkit'; +import { genkit, z } from 'genkit'; const ai = genkit({}); @@ -23,11 +23,11 @@ const ai = genkit({}); * genkit flow:run basic "\"hello\"" */ export const basic = ai.defineFlow('basic', async (subject) => { - const foo = await run('call-llm', async () => { + const foo = await ai.run('call-llm', async () => { return `subject: ${subject}`; }); - return await run('call-llm1', async () => { + return await ai.run('call-llm1', async () => { return `foo: ${foo}`; }); }); @@ -42,11 +42,11 @@ export const parent = ai.defineFlow( export const withInputSchema = ai.defineFlow( { name: 'withInputSchema', inputSchema: z.object({ subject: z.string() }) }, async (input) => { - const foo = await run('call-llm', async () => { + const foo = await ai.run('call-llm', async () => { return `subject: ${input.subject}`; }); - return await run('call-llm1', async () => { + return await ai.run('call-llm1', async () => { return `foo: ${foo}`; }); } @@ -56,15 +56,14 @@ export const withContext = ai.defineFlow( { name: 'withContext', inputSchema: z.object({ subject: z.string() }), - authPolicy: () => {}, }, - async (input) => { - return `subject: ${input.subject}, context: ${JSON.stringify(getFlowAuth())}`; + async (input, { context }) => { + return `subject: ${input.subject}, context: ${JSON.stringify(context)}`; } ); // genkit flow:run streamy 5 -s -export const streamy = ai.defineStreamingFlow( +export const streamy = ai.defineFlow( { name: 'streamy', inputSchema: z.number(), @@ -84,7 +83,7 @@ export const streamy = ai.defineStreamingFlow( ); // genkit flow:run streamy 5 -s -export const streamyThrowy = ai.defineStreamingFlow( +export const streamyThrowy = ai.defineFlow( { name: 'streamyThrowy', inputSchema: z.number(), @@ -113,13 +112,13 @@ export const streamyThrowy = ai.defineStreamingFlow( export const throwy = ai.defineFlow( { name: 'throwy', inputSchema: z.string(), outputSchema: z.string() }, async (subject) => { - const foo = await run('call-llm', async () => { + const foo = await ai.run('call-llm', async () => { return `subject: ${subject}`; }); if (subject) { throw new Error(subject); } - return await run('call-llm', async () => { + return await ai.run('call-llm', async () => { return `foo: ${foo}`; }); } @@ -132,13 +131,13 @@ export const throwy = ai.defineFlow( export const throwy2 = ai.defineFlow( { name: 'throwy2', inputSchema: z.string(), outputSchema: z.string() }, async (subject) => { - const foo = await run('call-llm', async () => { + const foo = await ai.run('call-llm', async () => { if (subject) { throw new Error(subject); } return `subject: ${subject}`; }); - return await run('call-llm', async () => { + return await ai.run('call-llm', async () => { return `foo: ${foo}`; }); } @@ -149,13 +148,13 @@ export const flowMultiStepCaughtError = ai.defineFlow( async (input) => { let i = 1; - const result1 = await run('step1', async () => { + const result1 = await ai.run('step1', async () => { return `${input} ${i++},`; }); let result2 = ''; try { - result2 = await run('step2', async () => { + result2 = await ai.run('step2', async () => { if (result1) { throw new Error('Got an error!'); } @@ -163,7 +162,7 @@ export const flowMultiStepCaughtError = ai.defineFlow( }); } catch (e) {} - return await run('step3', async () => { + return await ai.run('step3', async () => { return `${result2} ${i++}`; }); } @@ -172,19 +171,19 @@ export const flowMultiStepCaughtError = ai.defineFlow( export const multiSteps = ai.defineFlow( { name: 'multiSteps', inputSchema: z.string(), outputSchema: z.number() }, async (input) => { - const out1 = await run('step1', async () => { + const out1 = await ai.run('step1', async () => { return `Hello, ${input}! step 1`; }); - await run('step1', async () => { + await ai.run('step1', async () => { return `Hello2222, ${input}! step 1`; }); - const out2 = await run('step2', out1, async () => { + const out2 = await ai.run('step2', out1, async () => { return out1 + ' Faf '; }); - const out3 = await run('step3-array', async () => { + const out3 = await ai.run('step3-array', async () => { return [out2, out2]; }); - const out4 = await run('step4-num', async () => { + const out4 = await ai.run('step4-num', async () => { return out3.join('-()-'); }); return 42; @@ -192,16 +191,16 @@ export const multiSteps = ai.defineFlow( ); export const largeSteps = ai.defineFlow({ name: 'largeSteps' }, async () => { - await run('large-step1', async () => { + await ai.run('large-step1', async () => { return generateString(100_000); }); - await run('large-step2', async () => { + await ai.run('large-step2', async () => { return generateString(800_000); }); - await run('large-step3', async () => { + await ai.run('large-step3', async () => { return generateString(900_000); }); - await run('large-step4', async () => { + await ai.run('large-step4', async () => { return generateString(999_000); }); return 'something...'; diff --git a/js/testapps/flow-simple-ai/src/index.ts b/js/testapps/flow-simple-ai/src/index.ts index 8231c82b2..419c18624 100644 --- a/js/testapps/flow-simple-ai/src/index.ts +++ b/js/testapps/flow-simple-ai/src/index.ts @@ -26,7 +26,7 @@ import { GoogleAIFileManager } from '@google/generative-ai/server'; import { AlwaysOnSampler } from '@opentelemetry/sdk-trace-base'; import { initializeApp } from 'firebase-admin/app'; import { getFirestore } from 'firebase-admin/firestore'; -import { GenerateResponseData, MessageSchema, genkit, run, z } from 'genkit'; +import { GenerateResponseData, MessageSchema, genkit, z } from 'genkit'; import { logger } from 'genkit/logging'; import { ModelMiddleware } from 'genkit/model'; import { PluginProvider } from 'genkit/plugin'; @@ -94,7 +94,7 @@ export const jokeFlow = ai.defineFlow( outputSchema: z.string(), }, async (input) => { - return await run('call-llm', async () => { + return await ai.run('call-llm', async () => { const llmResponse = await ai.generate({ model: input.modelName, config: { version: input.modelVersion }, @@ -112,7 +112,7 @@ export const drawPictureFlow = ai.defineFlow( outputSchema: z.string(), }, async (input) => { - return await run('call-llm', async () => { + return await ai.run('call-llm', async () => { const llmResponse = await ai.generate({ model: input.modelName, prompt: `Draw a picture of a ${input.object}.`, @@ -122,7 +122,7 @@ export const drawPictureFlow = ai.defineFlow( } ); -export const streamFlow = ai.defineStreamingFlow( +export const streamFlow = ai.defineFlow( { name: 'streamFlow', inputSchema: z.string(), @@ -160,7 +160,7 @@ const GameCharactersSchema = z.object({ .describe('Characters'), }); -export const streamJsonFlow = ai.defineStreamingFlow( +export const streamJsonFlow = ai.defineFlow( { name: 'streamJsonFlow', inputSchema: z.number(), @@ -269,7 +269,7 @@ export const vertexStreamer = ai.defineFlow( outputSchema: z.string(), }, async (input, streamingCallback) => { - return await run('call-llm', async () => { + return await ai.run('call-llm', async () => { const llmResponse = await ai.generate({ model: gemini15Flash, prompt: `Tell me a very long joke about ${input}.`, @@ -386,7 +386,7 @@ const jokeSubjectGenerator = ai.defineTool( } ); -export const toolCaller = ai.defineStreamingFlow( +export const toolCaller = ai.defineFlow( { name: 'toolCaller', outputSchema: z.string(), @@ -508,7 +508,7 @@ export const toolTester = ai.defineFlow( } ); -export const arrayStreamTester = ai.defineStreamingFlow( +export const arrayStreamTester = ai.defineFlow( { name: 'arrayStreamTester', inputSchema: z.string().nullish(), diff --git a/js/testapps/format-tester/src/index.ts b/js/testapps/format-tester/src/index.ts index 195b91469..84dcfb590 100644 --- a/js/testapps/format-tester/src/index.ts +++ b/js/testapps/format-tester/src/index.ts @@ -21,7 +21,7 @@ import { claude35SonnetV2, vertexAIModelGarden, } from '@genkit-ai/vertexai/modelgarden'; -import { CallableFlow, GenerateOptions, genkit, z } from 'genkit'; +import { Flow, GenerateOptions, genkit, z } from 'genkit'; import { logger } from 'genkit/logging'; logger.setLogLevel('debug'); @@ -142,7 +142,7 @@ const prompts: Record = { }, }; -const flows: CallableFlow[] = []; +const flows: Flow[] = []; for (const format in prompts) { flows.push(formatFlow(format, prompts[format])); } @@ -165,7 +165,7 @@ async function main() { await flow(model); } catch (e: any) { console.error('ERROR:', e.stack); - fails.push({ model, flow: flow.flow.name, error: e.message }); + fails.push({ model, flow: flow.__action.name, error: e.message }); } } } diff --git a/js/testapps/langchain/src/index.ts b/js/testapps/langchain/src/index.ts index c228f02cb..f10553e6f 100644 --- a/js/testapps/langchain/src/index.ts +++ b/js/testapps/langchain/src/index.ts @@ -24,7 +24,7 @@ import { RunnablePassthrough, RunnableSequence, } from '@langchain/core/runnables'; -import { genkit, run, z } from 'genkit'; +import { genkit, z } from 'genkit'; import { GenkitTracer } from 'genkitx-langchain'; import { ollama } from 'genkitx-ollama'; import { PDFLoader } from 'langchain/document_loaders/fs/pdf'; @@ -51,10 +51,10 @@ const model = new GoogleVertexAI(); export const indexPdf = ai.defineFlow( { name: 'indexPdf', inputSchema: z.string(), outputSchema: z.void() }, async (filePath) => { - const docs = await run('load-pdf', async () => { + const docs = await ai.run('load-pdf', async () => { return await new PDFLoader(filePath).load(); }); - await run('index', async () => { + await ai.run('index', async () => { vectorStore.addDocuments(docs); }); } @@ -83,7 +83,3 @@ export const pdfQA = ai.defineFlow( return await chain.invoke(question, { callbacks: [new GenkitTracer()] }); } ); - -ai.startFlowServer({ - flows: [pdfQA], -}); diff --git a/js/testapps/prompt-file/src/index.ts b/js/testapps/prompt-file/src/index.ts index c94e8d3f2..f1596d8cc 100644 --- a/js/testapps/prompt-file/src/index.ts +++ b/js/testapps/prompt-file/src/index.ts @@ -77,7 +77,7 @@ ai.defineFlow( // A variation that supports streaming, optionally -ai.defineStreamingFlow( +ai.defineFlow( { name: 'tellStory', inputSchema: z.object({ diff --git a/js/testapps/rag/src/pdf-rag-firebase.ts b/js/testapps/rag/src/pdf-rag-firebase.ts index 158d873ad..2770858f1 100644 --- a/js/testapps/rag/src/pdf-rag-firebase.ts +++ b/js/testapps/rag/src/pdf-rag-firebase.ts @@ -21,7 +21,7 @@ import { FieldValue } from '@google-cloud/firestore'; import { initializeApp } from 'firebase-admin/app'; import { getFirestore } from 'firebase-admin/firestore'; import { readFile } from 'fs/promises'; -import { run, z } from 'genkit'; +import { z } from 'genkit'; import { chunk } from 'llm-chunk'; import path from 'path'; import pdf from 'pdf-parse'; @@ -119,16 +119,16 @@ export const indexPdfFirebase = ai.defineFlow( }, async (filePath) => { filePath = path.resolve(filePath); - const pdfTxt = await run('extract-text', () => + const pdfTxt = await ai.run('extract-text', () => extractTextFromPdf(filePath) ); - const chunks = await run('chunk-it', async () => + const chunks = await ai.run('chunk-it', async () => chunk(pdfTxt, chunkingConfig) ); // Add chunks to the index. - await run('index-chunks', async () => indexToFirestore(chunks)); + await ai.run('index-chunks', async () => indexToFirestore(chunks)); } ); diff --git a/js/testapps/rag/src/pdf-rag.ts b/js/testapps/rag/src/pdf-rag.ts index 94df972c9..e3b33ca5f 100644 --- a/js/testapps/rag/src/pdf-rag.ts +++ b/js/testapps/rag/src/pdf-rag.ts @@ -20,7 +20,7 @@ import { } from '@genkit-ai/dev-local-vectorstore'; import { gemini15Flash } from '@genkit-ai/vertexai'; import fs from 'fs'; -import { Document, run, z } from 'genkit'; +import { Document, z } from 'genkit'; import { chunk } from 'llm-chunk'; import path from 'path'; import pdf from 'pdf-parse'; @@ -74,9 +74,9 @@ export const indexPdf = ai.defineFlow( }, async (filePath) => { filePath = path.resolve(filePath); - const pdfTxt = await run('extract-text', () => extractText(filePath)); + const pdfTxt = await ai.run('extract-text', () => extractText(filePath)); - const chunks = await run('chunk-it', async () => + const chunks = await ai.run('chunk-it', async () => chunk(pdfTxt, chunkingConfig) ); @@ -108,9 +108,9 @@ export const synthesizeQuestions = ai.defineFlow( }, async (filePath) => { filePath = path.resolve(filePath); - const pdfTxt = await run('extract-text', () => extractText(filePath)); + const pdfTxt = await ai.run('extract-text', () => extractText(filePath)); - const chunks = await run('chunk-it', async () => + const chunks = await ai.run('chunk-it', async () => chunk(pdfTxt, chunkingConfig) ); @@ -127,7 +127,3 @@ export const synthesizeQuestions = ai.defineFlow( return questions; } ); - -ai.startFlowServer({ - flows: [pdfQA], -}); diff --git a/js/testapps/vertexai-reranker/src/index.ts b/js/testapps/vertexai-reranker/src/index.ts index 60278fef8..b27e25f9c 100644 --- a/js/testapps/vertexai-reranker/src/index.ts +++ b/js/testapps/vertexai-reranker/src/index.ts @@ -87,7 +87,3 @@ export const rerankFlow = ai.defineFlow( })); } ); - -ai.startFlowServer({ - flows: [rerankFlow], -}); diff --git a/js/testapps/vertexai-vector-search-bigquery/src/index.ts b/js/testapps/vertexai-vector-search-bigquery/src/index.ts index a2b13b6ee..9e508ce88 100644 --- a/js/testapps/vertexai-vector-search-bigquery/src/index.ts +++ b/js/testapps/vertexai-vector-search-bigquery/src/index.ts @@ -164,7 +164,3 @@ export const queryFlow = ai.defineFlow( }; } ); - -ai.startFlowServer({ - flows: [indexFlow], -}); diff --git a/js/testapps/vertexai-vector-search-custom/src/index.ts b/js/testapps/vertexai-vector-search-custom/src/index.ts index 581fa5079..d97f8808a 100644 --- a/js/testapps/vertexai-vector-search-custom/src/index.ts +++ b/js/testapps/vertexai-vector-search-custom/src/index.ts @@ -233,7 +233,3 @@ export const queryFlow = ai.defineFlow( }; } ); - -ai.startFlowServer({ - flows: [indexFlow, queryFlow], -}); diff --git a/js/testapps/vertexai-vector-search-firestore/src/index.ts b/js/testapps/vertexai-vector-search-firestore/src/index.ts index bad5aba88..1915d4654 100644 --- a/js/testapps/vertexai-vector-search-firestore/src/index.ts +++ b/js/testapps/vertexai-vector-search-firestore/src/index.ts @@ -167,7 +167,3 @@ export const queryFlow = ai.defineFlow( }; } ); - -ai.startFlowServer({ - flows: [indexFlow, queryFlow], -}); From 817be1796f51f2781a54870dc9add8da286b1ff5 Mon Sep 17 00:00:00 2001 From: Hugo Aguirre Date: Mon, 13 Jan 2025 18:21:56 +0000 Subject: [PATCH 176/562] fix(go): Fix TestActionTracing test case (#1595) --- go/core/action_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/go/core/action_test.go b/go/core/action_test.go index 5bf24b5a0..bdb64db0c 100644 --- a/go/core/action_test.go +++ b/go/core/action_test.go @@ -17,6 +17,7 @@ package core import ( "bytes" "context" + "fmt" "slices" "testing" @@ -114,17 +115,19 @@ func TestActionTracing(t *testing.T) { if err != nil { t.Fatal(err) } + provider := "test" tc := tracing.NewTestOnlyTelemetryClient() r.TracingState().WriteTelemetryImmediate(tc) const actionName = "TestTracing-inc" - a := defineAction(r, "test", actionName, atype.Custom, nil, nil, inc) + a := defineAction(r, provider, actionName, atype.Custom, nil, nil, inc) if _, err := a.Run(context.Background(), 3, nil); err != nil { t.Fatal(err) } // The same trace store is used for all tests, so there might be several traces. // Look for this one, which has a unique name. + fullActionName := fmt.Sprintf("%s/%s", provider, actionName) for _, td := range tc.Traces { - if td.DisplayName == actionName { + if td.DisplayName == fullActionName { // Spot check: expect a single span. if g, w := len(td.Spans), 1; g != w { t.Errorf("got %d spans, want %d", g, w) From 1bf90ad6f2e67392ac4c97096ccae48e628db3d4 Mon Sep 17 00:00:00 2001 From: Hugo Aguirre Date: Mon, 13 Jan 2025 22:28:56 +0000 Subject: [PATCH 177/562] feature(go): update flow streaming protocol to SSE (#1316) --- go/genkit/servers.go | 18 +++++-- go/samples/flow-sample1/main.go | 16 +++++- go/tests/api_test.go | 3 +- go/tests/test_app/main.go | 25 ++++++++- tests/flow_server_tests.yaml | 13 +++++ tests/package.json | 5 +- tests/reflection_api_tests.yaml | 4 ++ tests/src/flow_server_test.ts | 95 +++++++++++++++++++++++++++++++++ tests/test_js_app/src/index.ts | 20 +++++++ 9 files changed, 190 insertions(+), 9 deletions(-) create mode 100644 tests/flow_server_tests.yaml create mode 100644 tests/src/flow_server_test.ts diff --git a/go/genkit/servers.go b/go/genkit/servers.go index 433773090..9dd175328 100644 --- a/go/genkit/servers.go +++ b/go/genkit/servers.go @@ -399,12 +399,13 @@ func nonDurableFlowHandler(f flow) func(http.ResponseWriter, *http.Request) erro return err } var callback streamingCallback[json.RawMessage] - if stream { + if r.Header.Get("Accept") == "text/event-stream" || stream { w.Header().Set("Content-Type", "text/plain") w.Header().Set("Transfer-Encoding", "chunked") - // Stream results are newline-separated JSON. + // Event Stream results are in JSON format separated by two newline escape sequences + // including the `data` and `message` labels callback = func(ctx context.Context, msg json.RawMessage) error { - _, err := fmt.Fprintf(w, "%s\n", msg) + _, err := fmt.Fprintf(w, "data: {\"message\": %s}\n\n", msg) if err != nil { return err } @@ -417,8 +418,19 @@ func nonDurableFlowHandler(f flow) func(http.ResponseWriter, *http.Request) erro // TODO: telemetry out, err := f.runJSON(r.Context(), r.Header.Get("Authorization"), body.Data, callback) if err != nil { + if r.Header.Get("Accept") == "text/event-stream" || stream { + _, err = fmt.Fprintf(w, "data: {\"error\": {\"status\": \"INTERNAL\", \"message\": \"stream flow error\", \"details\": \"%v\"}}\n\n", err) + return err + } return err } + // Responses for streaming, non-durable flows should be prefixed + // with "data" + if r.Header.Get("Accept") == "text/event-stream" || stream { + _, err = fmt.Fprintf(w, "data: {\"result\": %s}\n\n", out) + return err + } + // Responses for non-streaming, non-durable flows are passed back // with the flow result stored in a field called "result." _, err = fmt.Fprintf(w, `{"result": %s}\n`, out) diff --git a/go/samples/flow-sample1/main.go b/go/samples/flow-sample1/main.go index 6fc848e71..1906889a7 100644 --- a/go/samples/flow-sample1/main.go +++ b/go/samples/flow-sample1/main.go @@ -37,7 +37,6 @@ import ( "encoding/json" "errors" "fmt" - "log" "strconv" @@ -106,6 +105,21 @@ func main() { return fmt.Sprintf("done: %d, streamed: %d times", count, i), nil }) + genkit.DefineStreamingFlow(g, "streamyThrowy", func(ctx context.Context, count int, cb func(context.Context, chunk) error) (string, error) { + i := 0 + if cb != nil { + for ; i < count; i++ { + if i == 3 { + return "", errors.New("boom!") + } + if err := cb(ctx, chunk{i}); err != nil { + return "", err + } + } + } + return fmt.Sprintf("done: %d, streamed: %d times", count, i), nil + }) + if err := g.Start(context.Background(), nil); err != nil { log.Fatal(err) } diff --git a/go/tests/api_test.go b/go/tests/api_test.go index b5f24df7e..b4def2945 100644 --- a/go/tests/api_test.go +++ b/go/tests/api_test.go @@ -45,8 +45,7 @@ type test struct { const hostPort = "http://localhost:3100" func TestReflectionAPI(t *testing.T) { - filenames, err := filepath.Glob(filepath.FromSlash("../../tests/*.yaml")) - + filenames, err := filepath.Glob(filepath.FromSlash("../../tests/reflection_api_tests.yaml")) if err != nil { t.Fatal(err) } diff --git a/go/tests/test_app/main.go b/go/tests/test_app/main.go index b36dcc82e..78821235b 100644 --- a/go/tests/test_app/main.go +++ b/go/tests/test_app/main.go @@ -19,6 +19,7 @@ package main import ( "context" "encoding/json" + "fmt" "log" "github.com/firebase/genkit/go/ai" @@ -26,6 +27,15 @@ import ( ) func main() { + opts := genkit.StartOptions{ + FlowAddr: "127.0.0.1:3400", + } + + // used for streamed flows + type chunk struct { + Count int `json:"count"` + } + g, err := genkit.New(nil) if err != nil { log.Fatal(err) @@ -39,7 +49,20 @@ func main() { _ = res return "TBD", nil }) - if err := g.Start(context.Background(), nil); err != nil { + + genkit.DefineStreamingFlow(g, "streamy", func(ctx context.Context, count int, cb func(context.Context, chunk) error) (string, error) { + i := 0 + if cb != nil { + for ; i < count; i++ { + if err := cb(ctx, chunk{i}); err != nil { + return "", err + } + } + } + return fmt.Sprintf("done %d, streamed: %d times", count, i), nil + }) + + if err := g.Start(context.Background(), &opts); err != nil { log.Fatal(err) } } diff --git a/tests/flow_server_tests.yaml b/tests/flow_server_tests.yaml new file mode 100644 index 000000000..529deb9c2 --- /dev/null +++ b/tests/flow_server_tests.yaml @@ -0,0 +1,13 @@ +# This file describes the responses to HTTP requests made +# to the flow server + +# TODO: add more test cases + +app: flow_server +tests: + - path: streamy + post: + data: 5 + response: + message: '{"count":{count}}' + result: 'done {count}, streamed: {count} times' diff --git a/tests/package.json b/tests/package.json index ea4204d38..cf43300c2 100644 --- a/tests/package.json +++ b/tests/package.json @@ -4,9 +4,10 @@ "description": "", "main": "lib/e2e.js", "scripts": { - "test": "npm-run-all test:reflection_api", + "test": "npm-run-all test:flow_server test:reflection_api", "test:dev_ui_test": "node --import tsx src/dev_ui_test.ts", - "test:reflection_api": "node --import tsx src/reflection_api_test.ts" + "test:reflection_api": "node --import tsx src/reflection_api_test.ts", + "test:flow_server": "node --import tsx src/flow_server_test.ts" }, "keywords": [], "author": "", diff --git a/tests/reflection_api_tests.yaml b/tests/reflection_api_tests.yaml index 060ce3302..a33309a92 100644 --- a/tests/reflection_api_tests.yaml +++ b/tests/reflection_api_tests.yaml @@ -876,3 +876,7 @@ tests: /flow/testFlow: key: /flow/testFlow name: testFlow + + /flow/streamy: + key: /flow/streamy + name: streamy diff --git a/tests/src/flow_server_test.ts b/tests/src/flow_server_test.ts new file mode 100644 index 000000000..06d83c943 --- /dev/null +++ b/tests/src/flow_server_test.ts @@ -0,0 +1,95 @@ +/** + * Copyright 2024 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 { readFileSync } from 'fs'; +import { streamFlow } from 'genkit/client'; +import * as yaml from 'yaml'; +import { retriable, runTestsForApp } from './utils.js'; + +(async () => { + // TODO: Add NodeJS tests + // Run the tests for go test app + await runTestsForApp('../go/tests/test_app', 'go run main.go', async () => { + await testFlowServer(); + console.log('Flow server tests done! \\o/'); + }); +})(); + +type TestResults = { + message: string; + result: string; +}; + +async function testFlowServer() { + const url = 'http://localhost:3400'; + await retriable( + async () => { + const res = await fetch(`${url}/streamy`, { + method: 'POST', + body: JSON.stringify({ + data: 1, + }), + }); + if (res.status != 200) { + throw new Error(`timed out waiting for flow server to become healthy`); + } + }, + { + maxRetries: 30, + delayMs: 1000, + } + ); + + const t = yaml.parse(readFileSync('flow_server_tests.yaml', 'utf8')); + for (const test of t.tests) { + let chunkCount = 0; + let expected: string = ''; + let want: TestResults = { + message: test.response.message, + result: test.response.result, + }; + console.log(`checking stream for: ${test.path}`); + (async () => { + const response = await streamFlow({ + url: `${url}/${test.path}`, + input: test.post.data, + }); + + for await (const chunk of response.stream()) { + expected = want.message.replace('{count}', chunkCount.toString()); + let chunkJSON = JSON.stringify(await chunk); + if (chunkJSON != expected) { + throw new Error( + `unexpected chunk data received, got: ${chunkJSON}, want: ${want.message}` + ); + } + chunkCount++; + } + if (chunkCount != test.post.data) { + throw new Error( + `unexpected number of stream chunks received: got ${chunkCount}, want: ${test.post.data}` + ); + } + let out = await response.output(); + want.result = want.result.replace(/\{count\}/g, chunkCount.toString()); + if (out != want.result) { + throw new Error( + `unexpected output received, got: ${out}, want: ${want.result}` + ); + } + })(); + } +} diff --git a/tests/test_js_app/src/index.ts b/tests/test_js_app/src/index.ts index 2653dc1a7..43ff62d6c 100644 --- a/tests/test_js_app/src/index.ts +++ b/tests/test_js_app/src/index.ts @@ -60,3 +60,23 @@ export const testFlow = ai.defineFlow( return 'Test flow passed'; } ); + +// genkit flow:run streamy 5 -s +export const streamy = ai.defineStreamingFlow( + { + name: 'streamy', + inputSchema: z.number(), + outputSchema: z.string(), + streamSchema: z.object({ count: z.number() }), + }, + async (count, streamingCallback) => { + let i = 0; + if (streamingCallback) { + for (; i < count; i++) { + await new Promise((r) => setTimeout(r, 1000)); + streamingCallback({ count: i }); + } + } + return `done: ${count}, streamed: ${i} times`; + } +); From 9f34be47f6b8390c3f16875248f0616c357af0e9 Mon Sep 17 00:00:00 2001 From: Max Lord Date: Wed, 15 Jan 2025 12:42:28 -0500 Subject: [PATCH 178/562] fix(js): Including missing labels in chat spans * Setting helper type * Adding labels for sessionId and threadName * Including I/O in send spans --- js/ai/src/chat.ts | 30 +++++++++++++++---- .../google-cloud/src/gcpOpenTelemetry.ts | 4 +-- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/js/ai/src/chat.ts b/js/ai/src/chat.ts index e84131057..a98aa4887 100644 --- a/js/ai/src/chat.ts +++ b/js/ai/src/chat.ts @@ -15,7 +15,7 @@ */ import { StreamingCallback, z } from '@genkit-ai/core'; -import { runInNewSpan } from '@genkit-ai/core/tracing'; +import { SPAN_TYPE_ATTR, runInNewSpan } from '@genkit-ai/core/tracing'; import { GenerateOptions, GenerateResponse, @@ -137,8 +137,17 @@ export class Chat { return runWithSession(this.session.registry, this.session, () => runInNewSpan( this.session.registry, - { metadata: { name: 'send' } }, - async () => { + { + metadata: { + name: 'send', + }, + labels: { + [SPAN_TYPE_ATTR]: 'helper', + sessionId: this.session.id, + threadName: this.threadName, + }, + }, + async (metadata) => { let resolvedOptions: ChatGenerateOptions; let streamingCallback: | StreamingCallback @@ -164,6 +173,7 @@ export class Chat { messages: this.messages, ...resolvedOptions, }; + metadata.input = resolvedOptions; let response = await generate(this.session.registry, { ...request, onChunk: streamingCallback, @@ -175,6 +185,7 @@ export class Chat { config: response?.request?.config, }); await this.updateMessages(response.messages); + metadata.output = JSON.stringify(response); return response; } ) @@ -190,8 +201,15 @@ export class Chat { return runWithSession(this.session.registry, this.session, () => runInNewSpan( this.session.registry, - { metadata: { name: 'send' } }, - async () => { + { + metadata: { name: 'send' }, + labels: { + [SPAN_TYPE_ATTR]: 'helper', + sessionId: this.session.id, + threadName: this.threadName, + }, + }, + async (metadata) => { let resolvedOptions; // string @@ -210,6 +228,7 @@ export class Chat { CustomOptions >; } + metadata.input = resolvedOptions; const { response, stream } = await generateStream( this.session.registry, @@ -230,6 +249,7 @@ export class Chat { config: resolvedResponse?.request?.config, }); this.updateMessages(resolvedResponse.messages); + metadata.output = JSON.stringify(resolvedResponse); }), stream, }; diff --git a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts index 0bf79c69d..096e077d7 100644 --- a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts +++ b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts @@ -406,9 +406,7 @@ class AdjustingTraceExporter implements SpanExporter { private markFailedSpan(span: ReadableSpan): ReadableSpan { if ( span.attributes['genkit:state'] === 'error' && - (span.attributes['genkit:type'] === 'action' || - span.attributes['genkit:type'] === 'flowStep' || - span.attributes['genkit:type'] === 'helper') + !span.attributes['genkit:isRoot'] ) { if (!!span.attributes['genkit:name']) { span.attributes['genkit:failedSpan'] = span.attributes['genkit:name']; From 5d24e230b7f321e930772de27c88e49d65cf7c76 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 15 Jan 2025 11:08:39 -0800 Subject: [PATCH 179/562] [Feat][Js] Make ai.generateStream no longer require a double await (#1602) * Make ai.generateStream no longer require a double await * Fix error handling * Friggen annoying this bindings... * Simplify and fix tests * Move async utilities into a new @genkit-ai/core/async --- js/ai/src/formats/index.ts | 4 +- js/ai/src/generate.ts | 87 +++++---------------------- js/ai/tests/generate/generate_test.ts | 36 +++++++++++ js/core/package.json | 6 ++ js/core/src/async.ts | 84 ++++++++++++++++++++++++++ js/genkit/src/genkit.ts | 29 ++++----- js/genkit/tests/generate_test.ts | 2 +- package.json | 4 +- 8 files changed, 158 insertions(+), 94 deletions(-) create mode 100644 js/core/src/async.ts diff --git a/js/ai/src/formats/index.ts b/js/ai/src/formats/index.ts index 9d4bf78de..7de5e3875 100644 --- a/js/ai/src/formats/index.ts +++ b/js/ai/src/formats/index.ts @@ -72,7 +72,7 @@ export function resolveInstructions( export function injectInstructions( messages: MessageData[], - instructions: string | boolean | undefined + instructions: string | false | undefined ): MessageData[] { if (!instructions) return messages; @@ -88,7 +88,7 @@ export function injectInstructions( } const newPart: TextPart = { - text: instructions as string, + text: instructions, metadata: { purpose: 'output' }, }; diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index a6ad66b94..ff5c4c595 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -22,6 +22,7 @@ import { sentinelNoopStreamingCallback, z, } from '@genkit-ai/core'; +import { Channel } from '@genkit-ai/core/async'; import { Registry } from '@genkit-ai/core/registry'; import { toJsonSchema } from '@genkit-ai/core/schema'; import { DocumentData } from './document.js'; @@ -355,17 +356,7 @@ export interface GenerateStreamResponse { get response(): Promise>; } -function createPromise(): { - resolve: (result: T) => unknown; - reject: (err: unknown) => unknown; - promise: Promise; -} { - let resolve, reject; - let promise = new Promise((res, rej) => ([resolve, reject] = [res, rej])); - return { resolve, reject, promise }; -} - -export async function generateStream< +export function generateStream< O extends z.ZodTypeAny = z.ZodTypeAny, CustomOptions extends z.ZodTypeAny = typeof GenerationCommonConfigSchema, >( @@ -373,68 +364,22 @@ export async function generateStream< options: | GenerateOptions | PromiseLike> -): Promise> { - let firstChunkSent = false; - return new Promise>( - (initialResolve, initialReject) => { - const { - resolve: finalResolve, - reject: finalReject, - promise: finalPromise, - } = createPromise>(); - - let provideNextChunk, nextChunk; - ({ resolve: provideNextChunk, promise: nextChunk } = - createPromise()); - async function* chunkStream(): AsyncIterable { - while (true) { - const next = await nextChunk; - if (!next) break; - yield next; - } - } +): GenerateStreamResponse { + let channel = new Channel(); - try { - generate(registry, { - ...options, - onChunk: (chunk) => { - firstChunkSent = true; - provideNextChunk(chunk); - ({ resolve: provideNextChunk, promise: nextChunk } = - createPromise()); - }, - }) - .then((result) => { - provideNextChunk(null); - finalResolve(result); - }) - .catch((e) => { - if (!firstChunkSent) { - initialReject(e); - return; - } - provideNextChunk(null); - finalReject(e); - }); - } catch (e) { - if (!firstChunkSent) { - initialReject(e); - return; - } - provideNextChunk(null); - finalReject(e); - } - - initialResolve({ - get response() { - return finalPromise; - }, - get stream() { - return chunkStream(); - }, - }); - } + const generated = generate(registry, { + ...options, + onChunk: (chunk) => channel.send(chunk), + }); + generated.then( + () => channel.close(), + (err) => channel.error(err) ); + + return { + response: generated, + stream: channel, + }; } export function tagAsPreamble(msgs?: MessageData[]): MessageData[] | undefined { diff --git a/js/ai/tests/generate/generate_test.ts b/js/ai/tests/generate/generate_test.ts index 72c3a565a..c7ff5ec10 100644 --- a/js/ai/tests/generate/generate_test.ts +++ b/js/ai/tests/generate/generate_test.ts @@ -21,6 +21,7 @@ import { beforeEach, describe, it } from 'node:test'; import { GenerateOptions, generate, + generateStream, toGenerateRequest, } from '../../src/generate.js'; import { ModelAction, ModelMiddleware, defineModel } from '../../src/model.js'; @@ -355,6 +356,7 @@ describe('generate', () => { }) ); }); + it('should preserve the request in the returned response, enabling .messages', async () => { const response = await generate(registry, { model: 'echo', @@ -365,4 +367,38 @@ describe('generate', () => { ['Testing messages', 'Testing messages'] ); }); + + describe('generateStream', () => { + it('should pass a smoke test', async () => { + let registry = new Registry(); + + defineModel( + registry, + { name: 'echo-streaming', supports: { tools: true } }, + async (input, streamingCallback) => { + streamingCallback!({ content: [{ text: 'hello, ' }] }); + streamingCallback!({ content: [{ text: 'world!' }] }); + return { + message: input.messages[0], + finishReason: 'stop', + }; + } + ); + + const { response, stream } = generateStream(registry, { + model: 'echo-streaming', + prompt: 'Testing streaming', + }); + + let streamed: string[] = []; + for await (const chunk of stream) { + streamed.push(chunk.text); + } + assert.deepEqual(streamed, ['hello, ', 'world!']); + assert.deepEqual( + (await response).messages.map((m) => m.content[0].text), + ['Testing streaming', 'Testing streaming'] + ); + }); + }); }); diff --git a/js/core/package.json b/js/core/package.json index f0bfe1234..ae33f7e32 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -61,6 +61,12 @@ "import": "./lib/index.mjs", "default": "./lib/index.js" }, + "./async": { + "types": "./lib/async.ts", + "require": "./lib/async.js", + "import": "./lib/async.mjs", + "default": "./lib/async.js" + }, "./registry": { "types": "./lib/registry.d.ts", "require": "./lib/registry.js", diff --git a/js/core/src/async.ts b/js/core/src/async.ts new file mode 100644 index 000000000..6d1f9cc7b --- /dev/null +++ b/js/core/src/async.ts @@ -0,0 +1,84 @@ +/** + * 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. + */ + +/** + * A handle to a promise and its resolvers. + */ +export interface Task { + resolve: (result: T) => void; + reject: (err: unknown) => void; + promise: Promise; +} + +/** Utility for creating Tasks. */ +function createTask(): Task { + let resolve: unknown, reject: unknown; + let promise = new Promise((res, rej) => ([resolve, reject] = [res, rej])); + return { + resolve: resolve as Task['resolve'], + reject: reject as Task['reject'], + promise, + }; +} + +/** + * A class designed to help turn repeated callbacks into async iterators. + * Based loosely on a combination of Go channels and Promises. + */ +export class Channel implements AsyncIterable { + private ready: Task = createTask(); + private buffer: (T | null)[] = []; + private err: unknown = null; + + send(value: T): void { + this.buffer.push(value); + this.ready.resolve(); + } + + close(): void { + this.buffer.push(null); + this.ready.resolve(); + } + + error(err: unknown): void { + // Note: we cannot use this.ready.reject because it will be ignored + // if ready.resolved has already been called. + this.err = err; + } + + [Symbol.asyncIterator](): AsyncIterator { + return { + next: async (): Promise> => { + if (this.err) { + throw this.err; + } + + if (!this.buffer.length) { + await this.ready.promise; + } + const value = this.buffer.shift()!; + if (!this.buffer.length) { + this.ready = createTask(); + } + + return { + value, + done: !value, + }; + }, + }; + } +} diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index b71262592..af8f74db1 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -845,7 +845,7 @@ export class Genkit implements HasRegistry { * model: gemini15Flash, // default model * }) * - * const { response, stream } = await ai.generateStream('hi'); + * const { response, stream } = ai.generateStream('hi'); * for await (const chunk of stream) { * console.log(chunk.text); * } @@ -854,7 +854,7 @@ export class Genkit implements HasRegistry { */ generateStream( strPrompt: string - ): Promise>>; + ): GenerateStreamResponse>; /** * Make a streaming generate call to the default model with a multipart request. @@ -865,7 +865,7 @@ export class Genkit implements HasRegistry { * model: gemini15Flash, // default model * }) * - * const { response, stream } = await ai.generateStream([ + * const { response, stream } = ai.generateStream([ * { media: {url: 'http://....'} }, * { text: 'describe this image' } * ]); @@ -877,7 +877,7 @@ export class Genkit implements HasRegistry { */ generateStream( parts: Part[] - ): Promise>>; + ): GenerateStreamResponse>; /** * Streaming generate calls a generative model based on the provided prompt and configuration. If @@ -892,7 +892,7 @@ export class Genkit implements HasRegistry { * plugins: [googleAI()], * }) * - * const { response, stream } = await ai.generateStream({ + * const { response, stream } = ai.generateStream({ * system: 'talk like a pirate', * prompt: [ * { media: { url: 'http://....' } }, @@ -915,9 +915,9 @@ export class Genkit implements HasRegistry { parts: | GenerateOptions | PromiseLike> - ): Promise>>; + ): GenerateStreamResponse>; - async generateStream< + generateStream< O extends z.ZodTypeAny = z.ZodTypeAny, CustomOptions extends z.ZodTypeAny = typeof GenerationCommonConfigSchema, >( @@ -926,18 +926,11 @@ export class Genkit implements HasRegistry { | Part[] | GenerateStreamOptions | PromiseLike> - ): Promise>> { - let resolvedOptions: GenerateOptions; - if (options instanceof Promise) { - resolvedOptions = await options; - } else if (typeof options === 'string' || Array.isArray(options)) { - resolvedOptions = { - prompt: options, - }; - } else { - resolvedOptions = options as GenerateOptions; + ): GenerateStreamResponse> { + if (typeof options === 'string' || Array.isArray(options)) { + options = { prompt: options }; } - return generateStream(this.registry, resolvedOptions); + return generateStream(this.registry, options); } /** diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index 028ed2a38..653b96f00 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -172,7 +172,7 @@ describe('generate', () => { ); assert.rejects(async () => { - const { response, stream } = await ai.generateStream({ + const { response, stream } = ai.generateStream({ prompt: 'hi', model: 'blockingModel', }); diff --git a/package.json b/package.json index 7ec04e2ae..f509a80eb 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "scripts": { "preinstall": "npx only-allow pnpm", "setup": "npm-run-all pnpm-install-js pnpm-install-genkit-tools build link-genkit-cli", - "format": "(prettier . --write) && (ts-node scripts/copyright.ts)", - "format:check": "(prettier . --check) && (ts-node scripts/copyright.ts --check)", + "format": "(prettier . --write) && (tsx scripts/copyright.ts)", + "format:check": "(prettier . --check) && (tsx scripts/copyright.ts --check)", "build": "pnpm build:js && pnpm build:genkit-tools", "build:js": "cd js && pnpm i && pnpm build", "build:genkit-tools": "cd genkit-tools && pnpm i && pnpm build", From 10412f854752dc3a45778461459da6cbb2e208f4 Mon Sep 17 00:00:00 2001 From: thedmail Date: Wed, 15 Jan 2025 16:20:20 -0800 Subject: [PATCH 180/562] docs: Rolls back migration of these samples to the the Firebase repo. (#1564) * docs: Rolls back migration of these samples to the the Firebase repo. * Update README.md Results of running pnpm format * Delete .idea directory --- samples/README.md | 12 +- samples/chatbot/.gitignore | 1 + samples/chatbot/README.md | 35 ++++ samples/chatbot/eval.json | 10 ++ samples/chatbot/genkit-app/.editorconfig | 16 ++ samples/chatbot/genkit-app/.gitignore | 42 +++++ samples/chatbot/genkit-app/README.md | 27 +++ samples/chatbot/genkit-app/angular.json | 105 ++++++++++++ samples/chatbot/genkit-app/package.json | 44 +++++ samples/chatbot/genkit-app/public/favicon.ico | Bin 0 -> 15086 bytes .../genkit-app/src/app/app.component.html | 36 ++++ .../genkit-app/src/app/app.component.scss | 67 ++++++++ .../genkit-app/src/app/app.component.spec.ts | 47 ++++++ .../genkit-app/src/app/app.component.ts | 45 +++++ .../chatbot/genkit-app/src/app/app.config.ts | 31 ++++ .../chatbot/genkit-app/src/app/app.routes.ts | 26 +++ .../samples/chatbot/chatbot.component.html | 58 +++++++ .../samples/chatbot/chatbot.component.scss | 75 +++++++++ .../samples/chatbot/chatbot.component.spec.ts | 38 +++++ .../app/samples/chatbot/chatbot.component.ts | 157 +++++++++++++++++ samples/chatbot/genkit-app/src/index.html | 35 ++++ samples/chatbot/genkit-app/src/main.ts | 23 +++ samples/chatbot/genkit-app/src/styles.scss | 55 ++++++ samples/chatbot/genkit-app/tsconfig.app.json | 10 ++ samples/chatbot/genkit-app/tsconfig.json | 29 ++++ samples/chatbot/genkit-app/tsconfig.spec.json | 9 + samples/chatbot/package.json | 17 ++ samples/chatbot/server/package.json | 29 ++++ samples/chatbot/server/src/index.ts | 100 +++++++++++ samples/chatbot/server/src/memory.ts | 35 ++++ samples/chatbot/server/tsconfig.json | 14 ++ samples/js-angular/.gitignore | 1 + samples/js-angular/README.md | 24 +++ samples/js-angular/genkit-app/.editorconfig | 16 ++ samples/js-angular/genkit-app/.gitignore | 42 +++++ samples/js-angular/genkit-app/README.md | 27 +++ samples/js-angular/genkit-app/angular.json | 99 +++++++++++ samples/js-angular/genkit-app/package.json | 41 +++++ .../js-angular/genkit-app/public/favicon.ico | Bin 0 -> 15086 bytes .../genkit-app/src/app/app.component.html | 36 ++++ .../genkit-app/src/app/app.component.scss | 67 ++++++++ .../genkit-app/src/app/app.component.spec.ts | 47 ++++++ .../genkit-app/src/app/app.component.ts | 45 +++++ .../genkit-app/src/app/app.config.ts | 29 ++++ .../genkit-app/src/app/app.routes.ts | 36 ++++ .../src/app/home/home.component.html | 32 ++++ .../src/app/home/home.component.scss | 19 +++ .../src/app/home/home.component.spec.ts | 38 +++++ .../genkit-app/src/app/home/home.component.ts | 27 +++ .../samples/chatbot/chatbot.component.html | 77 +++++++++ .../samples/chatbot/chatbot.component.scss | 55 ++++++ .../samples/chatbot/chatbot.component.spec.ts | 38 +++++ .../app/samples/chatbot/chatbot.component.ts | 158 ++++++++++++++++++ .../streaming-json.component.html | 36 ++++ .../streaming-json.component.scss | 23 +++ .../streaming-json.component.spec.ts | 38 +++++ .../streaming-json.component.ts | 61 +++++++ samples/js-angular/genkit-app/src/index.html | 35 ++++ samples/js-angular/genkit-app/src/main.ts | 23 +++ samples/js-angular/genkit-app/src/styles.scss | 55 ++++++ .../js-angular/genkit-app/tsconfig.app.json | 10 ++ samples/js-angular/genkit-app/tsconfig.json | 29 ++++ .../js-angular/genkit-app/tsconfig.spec.json | 9 + samples/js-angular/package.json | 17 ++ samples/js-angular/server/package.json | 27 +++ samples/js-angular/server/src/agent.ts | 111 ++++++++++++ samples/js-angular/server/src/chatbot.ts | 75 +++++++++ samples/js-angular/server/src/genkit.ts | 22 +++ samples/js-angular/server/src/index.ts | 23 +++ .../js-angular/server/src/jsonStreaming.ts | 80 +++++++++ samples/js-angular/server/tsconfig.json | 14 ++ samples/js-coffee-shop/README.md | 6 + samples/js-coffee-shop/package.json | 35 ++++ samples/js-coffee-shop/src/index.ts | 129 ++++++++++++++ samples/js-coffee-shop/src/input.json | 7 + samples/js-coffee-shop/tsconfig.json | 15 ++ samples/js-menu/README.md | 27 +++ samples/js-menu/data/menu.jpeg | Bin 0 -> 493635 bytes samples/js-menu/data/menu.json | 97 +++++++++++ samples/js-menu/package.json | 30 ++++ samples/js-menu/src/01/example.json | 5 + samples/js-menu/src/01/prompts.ts | 87 ++++++++++ samples/js-menu/src/02/example.json | 3 + samples/js-menu/src/02/flows.ts | 33 ++++ samples/js-menu/src/02/prompts.ts | 45 +++++ samples/js-menu/src/02/tools.ts | 35 ++++ samples/js-menu/src/03/chats.ts | 58 +++++++ samples/js-menu/src/03/example.json | 4 + samples/js-menu/src/03/flows.ts | 96 +++++++++++ samples/js-menu/src/03/prompts.ts | 47 ++++++ .../src/04/example.indexMenuItems.json | 57 +++++++ .../js-menu/src/04/example.menuQuestion.json | 3 + samples/js-menu/src/04/flows.ts | 84 ++++++++++ samples/js-menu/src/04/prompts.ts | 44 +++++ .../src/05/example.visualMenuQuestion.json | 3 + samples/js-menu/src/05/flows.ts | 96 +++++++++++ samples/js-menu/src/05/prompts.ts | 61 +++++++ samples/js-menu/src/genkit.ts | 34 ++++ samples/js-menu/src/index.ts | 36 ++++ samples/js-menu/src/types.ts | 63 +++++++ samples/js-menu/tsconfig.json | 15 ++ samples/js-schoolAgent/README.md | 107 ++++++++++++ samples/js-schoolAgent/package.json | 33 ++++ .../js-schoolAgent/prompts/myPrompt.prompt | 1 + samples/js-schoolAgent/src/attendanceAgent.ts | 50 ++++++ samples/js-schoolAgent/src/data.ts | 96 +++++++++++ samples/js-schoolAgent/src/genkit.ts | 38 +++++ samples/js-schoolAgent/src/gradesAgent.ts | 53 ++++++ samples/js-schoolAgent/src/routingAgent.ts | 50 ++++++ samples/js-schoolAgent/src/terminal.ts | 123 ++++++++++++++ samples/js-schoolAgent/src/tools.ts | 147 ++++++++++++++++ samples/js-schoolAgent/src/types.ts | 26 +++ samples/js-schoolAgent/src/util.ts | 20 +++ samples/js-schoolAgent/tsconfig.json | 16 ++ samples/prompts/README.md | 9 + samples/prompts/package.json | 26 +++ samples/prompts/src/index.ts | 89 ++++++++++ samples/prompts/tsconfig.json | 14 ++ 118 files changed, 5021 insertions(+), 2 deletions(-) create mode 100644 samples/chatbot/.gitignore create mode 100644 samples/chatbot/README.md create mode 100644 samples/chatbot/eval.json create mode 100644 samples/chatbot/genkit-app/.editorconfig create mode 100644 samples/chatbot/genkit-app/.gitignore create mode 100644 samples/chatbot/genkit-app/README.md create mode 100644 samples/chatbot/genkit-app/angular.json create mode 100644 samples/chatbot/genkit-app/package.json create mode 100644 samples/chatbot/genkit-app/public/favicon.ico create mode 100644 samples/chatbot/genkit-app/src/app/app.component.html create mode 100644 samples/chatbot/genkit-app/src/app/app.component.scss create mode 100644 samples/chatbot/genkit-app/src/app/app.component.spec.ts create mode 100644 samples/chatbot/genkit-app/src/app/app.component.ts create mode 100644 samples/chatbot/genkit-app/src/app/app.config.ts create mode 100644 samples/chatbot/genkit-app/src/app/app.routes.ts create mode 100644 samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.html create mode 100644 samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.scss create mode 100644 samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts create mode 100644 samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts create mode 100644 samples/chatbot/genkit-app/src/index.html create mode 100644 samples/chatbot/genkit-app/src/main.ts create mode 100644 samples/chatbot/genkit-app/src/styles.scss create mode 100644 samples/chatbot/genkit-app/tsconfig.app.json create mode 100644 samples/chatbot/genkit-app/tsconfig.json create mode 100644 samples/chatbot/genkit-app/tsconfig.spec.json create mode 100644 samples/chatbot/package.json create mode 100644 samples/chatbot/server/package.json create mode 100644 samples/chatbot/server/src/index.ts create mode 100644 samples/chatbot/server/src/memory.ts create mode 100644 samples/chatbot/server/tsconfig.json create mode 100644 samples/js-angular/.gitignore create mode 100644 samples/js-angular/README.md create mode 100644 samples/js-angular/genkit-app/.editorconfig create mode 100644 samples/js-angular/genkit-app/.gitignore create mode 100644 samples/js-angular/genkit-app/README.md create mode 100644 samples/js-angular/genkit-app/angular.json create mode 100644 samples/js-angular/genkit-app/package.json create mode 100644 samples/js-angular/genkit-app/public/favicon.ico create mode 100644 samples/js-angular/genkit-app/src/app/app.component.html create mode 100644 samples/js-angular/genkit-app/src/app/app.component.scss create mode 100644 samples/js-angular/genkit-app/src/app/app.component.spec.ts create mode 100644 samples/js-angular/genkit-app/src/app/app.component.ts create mode 100644 samples/js-angular/genkit-app/src/app/app.config.ts create mode 100644 samples/js-angular/genkit-app/src/app/app.routes.ts create mode 100644 samples/js-angular/genkit-app/src/app/home/home.component.html create mode 100644 samples/js-angular/genkit-app/src/app/home/home.component.scss create mode 100644 samples/js-angular/genkit-app/src/app/home/home.component.spec.ts create mode 100644 samples/js-angular/genkit-app/src/app/home/home.component.ts create mode 100644 samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.html create mode 100644 samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.scss create mode 100644 samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts create mode 100644 samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts create mode 100644 samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.html create mode 100644 samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.scss create mode 100644 samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.spec.ts create mode 100644 samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts create mode 100644 samples/js-angular/genkit-app/src/index.html create mode 100644 samples/js-angular/genkit-app/src/main.ts create mode 100644 samples/js-angular/genkit-app/src/styles.scss create mode 100644 samples/js-angular/genkit-app/tsconfig.app.json create mode 100644 samples/js-angular/genkit-app/tsconfig.json create mode 100644 samples/js-angular/genkit-app/tsconfig.spec.json create mode 100644 samples/js-angular/package.json create mode 100644 samples/js-angular/server/package.json create mode 100644 samples/js-angular/server/src/agent.ts create mode 100644 samples/js-angular/server/src/chatbot.ts create mode 100644 samples/js-angular/server/src/genkit.ts create mode 100644 samples/js-angular/server/src/index.ts create mode 100644 samples/js-angular/server/src/jsonStreaming.ts create mode 100644 samples/js-angular/server/tsconfig.json create mode 100644 samples/js-coffee-shop/README.md create mode 100644 samples/js-coffee-shop/package.json create mode 100644 samples/js-coffee-shop/src/index.ts create mode 100644 samples/js-coffee-shop/src/input.json create mode 100644 samples/js-coffee-shop/tsconfig.json create mode 100644 samples/js-menu/README.md create mode 100644 samples/js-menu/data/menu.jpeg create mode 100644 samples/js-menu/data/menu.json create mode 100644 samples/js-menu/package.json create mode 100644 samples/js-menu/src/01/example.json create mode 100644 samples/js-menu/src/01/prompts.ts create mode 100644 samples/js-menu/src/02/example.json create mode 100644 samples/js-menu/src/02/flows.ts create mode 100644 samples/js-menu/src/02/prompts.ts create mode 100644 samples/js-menu/src/02/tools.ts create mode 100644 samples/js-menu/src/03/chats.ts create mode 100644 samples/js-menu/src/03/example.json create mode 100644 samples/js-menu/src/03/flows.ts create mode 100644 samples/js-menu/src/03/prompts.ts create mode 100644 samples/js-menu/src/04/example.indexMenuItems.json create mode 100644 samples/js-menu/src/04/example.menuQuestion.json create mode 100644 samples/js-menu/src/04/flows.ts create mode 100644 samples/js-menu/src/04/prompts.ts create mode 100644 samples/js-menu/src/05/example.visualMenuQuestion.json create mode 100644 samples/js-menu/src/05/flows.ts create mode 100644 samples/js-menu/src/05/prompts.ts create mode 100644 samples/js-menu/src/genkit.ts create mode 100644 samples/js-menu/src/index.ts create mode 100644 samples/js-menu/src/types.ts create mode 100644 samples/js-menu/tsconfig.json create mode 100644 samples/js-schoolAgent/README.md create mode 100644 samples/js-schoolAgent/package.json create mode 100644 samples/js-schoolAgent/prompts/myPrompt.prompt create mode 100644 samples/js-schoolAgent/src/attendanceAgent.ts create mode 100644 samples/js-schoolAgent/src/data.ts create mode 100644 samples/js-schoolAgent/src/genkit.ts create mode 100644 samples/js-schoolAgent/src/gradesAgent.ts create mode 100644 samples/js-schoolAgent/src/routingAgent.ts create mode 100644 samples/js-schoolAgent/src/terminal.ts create mode 100644 samples/js-schoolAgent/src/tools.ts create mode 100644 samples/js-schoolAgent/src/types.ts create mode 100644 samples/js-schoolAgent/src/util.ts create mode 100644 samples/js-schoolAgent/tsconfig.json create mode 100644 samples/prompts/README.md create mode 100644 samples/prompts/package.json create mode 100644 samples/prompts/src/index.ts create mode 100644 samples/prompts/tsconfig.json diff --git a/samples/README.md b/samples/README.md index 333412ade..c106a1468 100644 --- a/samples/README.md +++ b/samples/README.md @@ -1,4 +1,12 @@ # Genkit samples -These samples have migrated to the Firebase -[`quickstart-nodejs` repo](https://github.com/firebase/quickstart-nodejs). +Take a look at some samples of Genkit in use: + +- [js-coffee-shop](js-coffee-shop/): "AI barista", demonstrating simple + LLM usage +- [js-menu](js-menu/): Progressively more sophisticated versions of a + menu understanding app +- [chatbot](chatbot/): A simple chatbot with a JavaScript frontend +- [js-angular](js-angular/): Demo of streaming to an Angular frontend +- [js-schoolAgent](js-schoolAgent/): A simple school assistant system with a routing agent and specialized agents +- [prompts](prompts/): Shows off several prompting techniques diff --git a/samples/chatbot/.gitignore b/samples/chatbot/.gitignore new file mode 100644 index 000000000..7951405f8 --- /dev/null +++ b/samples/chatbot/.gitignore @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/samples/chatbot/README.md b/samples/chatbot/README.md new file mode 100644 index 000000000..a2f1728bf --- /dev/null +++ b/samples/chatbot/README.md @@ -0,0 +1,35 @@ +# Chatbot + +This is a simple chatbot. You can pick which model to use. + +Prerequisite + +- install Genkit (`npm i -g genkit`) +- Google Cloud project with Vertex AI API enabled (https://pantheon.corp.google.com/apis/library/aiplatform.googleapis.com) +- gcloud CLI installed (https://cloud.google.com/sdk/docs/install-sdk) +- to use Llama 3.1 405b enable it in the Vertex AI [Model Garden](https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama3-405b-instruct-maas) + +The sample is using Vertex AI, so you'll need to auth: + +```bash +gcloud auth login +gcloud auth application-default login --project YOUR_PROJECT +``` + +Clone this code + +``` +git clone https://github.com/firebase/genkit +cd genkit/samples/chatbot +``` + +Install deps and run the chatbot app + +```bash +npm run setup +npm start +``` + +Point your browser to http://localhost:4200/ + +Inspect runs in http://localhost:4000/ diff --git a/samples/chatbot/eval.json b/samples/chatbot/eval.json new file mode 100644 index 000000000..ca1c84cba --- /dev/null +++ b/samples/chatbot/eval.json @@ -0,0 +1,10 @@ +[ + { + "conversationId": "1234", + "prompt": "tell me a joke" + }, + { + "conversationId": "2345", + "prompt": "wtite a python program that prints out weather for the current location" + } +] diff --git a/samples/chatbot/genkit-app/.editorconfig b/samples/chatbot/genkit-app/.editorconfig new file mode 100644 index 000000000..59d9a3a3e --- /dev/null +++ b/samples/chatbot/genkit-app/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/samples/chatbot/genkit-app/.gitignore b/samples/chatbot/genkit-app/.gitignore new file mode 100644 index 000000000..cc7b14135 --- /dev/null +++ b/samples/chatbot/genkit-app/.gitignore @@ -0,0 +1,42 @@ +# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files +.DS_Store +Thumbs.db diff --git a/samples/chatbot/genkit-app/README.md b/samples/chatbot/genkit-app/README.md new file mode 100644 index 000000000..0aeb2095b --- /dev/null +++ b/samples/chatbot/genkit-app/README.md @@ -0,0 +1,27 @@ +# GenkitApp + +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 18.0.2. + +## Development server + +Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. + +## Code scaffolding + +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. + +## Build + +Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Running unit tests + +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Running end-to-end tests + +Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/samples/chatbot/genkit-app/angular.json b/samples/chatbot/genkit-app/angular.json new file mode 100644 index 000000000..07bb6ad5f --- /dev/null +++ b/samples/chatbot/genkit-app/angular.json @@ -0,0 +1,105 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "genkit-app": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:application", + "options": { + "outputPath": "dist/genkit-app", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "@angular/material/prebuilt-themes/azure-blue.css", + "src/styles.scss", + "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.css" + ], + "scripts": [ + "node_modules/prismjs/prism.js", + "node_modules/prismjs/components/prism-csharp.min.js", + "node_modules/prismjs/components/prism-css.min.js", + "node_modules/prismjs/plugins/line-highlight/prism-line-highlight.js" + ] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kB", + "maximumError": "1MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kB", + "maximumError": "4kB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "genkit-app:build:production" + }, + "development": { + "buildTarget": "genkit-app:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": ["zone.js", "zone.js/testing"], + "tsConfig": "tsconfig.spec.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "@angular/material/prebuilt-themes/azure-blue.css", + "src/styles.scss" + ], + "scripts": [] + } + } + } + } + } +} diff --git a/samples/chatbot/genkit-app/package.json b/samples/chatbot/genkit-app/package.json new file mode 100644 index 000000000..465379b0b --- /dev/null +++ b/samples/chatbot/genkit-app/package.json @@ -0,0 +1,44 @@ +{ + "name": "genkit-app", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "private": true, + "dependencies": { + "@angular/animations": "^18.0.0", + "@angular/cdk": "^18.0.1", + "@angular/common": "^18.0.0", + "@angular/compiler": "^18.0.0", + "@angular/core": "^18.0.0", + "@angular/forms": "^18.0.0", + "@angular/material": "^18.0.1", + "@angular/platform-browser": "^18.0.0", + "@angular/platform-browser-dynamic": "^18.0.0", + "@angular/router": "^18.0.0", + "marked": "^12.0.2", + "ngx-markdown": "^18.0.0", + "prismjs": "^1.29.0", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.14.3", + "genkit": "^0.9.0-rc || ^0.9" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^18.0.2", + "@angular/cli": "^18.0.2", + "@angular/compiler-cli": "^18.0.0", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.1.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.4.2" + } +} diff --git a/samples/chatbot/genkit-app/public/favicon.ico b/samples/chatbot/genkit-app/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..57614f9c967596fad0a3989bec2b1deff33034f6 GIT binary patch literal 15086 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 Nr__JTFzvMr^FKp4S3v*( literal 0 HcmV?d00001 diff --git a/samples/chatbot/genkit-app/src/app/app.component.html b/samples/chatbot/genkit-app/src/app/app.component.html new file mode 100644 index 000000000..78c45a375 --- /dev/null +++ b/samples/chatbot/genkit-app/src/app/app.component.html @@ -0,0 +1,36 @@ + + +

diff --git a/samples/chatbot/genkit-app/src/app/app.component.scss b/samples/chatbot/genkit-app/src/app/app.component.scss new file mode 100644 index 000000000..0e0b80557 --- /dev/null +++ b/samples/chatbot/genkit-app/src/app/app.component.scss @@ -0,0 +1,67 @@ +/** + * Copyright 2024 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. + */ + +app-bar { + border-bottom: 1px solid var(--divider-color); + grid-area: header; +} + +article { + grid-area: content; +} + +.wrapper { + display: grid; + grid-template: + 'header' auto + 'content' 1fr + / 1fr; + height: 100vh; +} + +.home-link { + align-items: center; + color: var(--mat-app-color); + display: flex; + gap: 8px; + + img { + height: 22px; + padding-left: 4px; + } +} + +.mat-toolbar { + background: #d7e3ff; + color: #005cbb; + gap: 4px; +} + +nav { + --mdc-secondary-navigation-tab-container-height: 64px; + --mat-tab-header-divider-height: 0; + margin-left: 32px; +} + +.preview-badge { + margin-left: 8px; + + mat-icon { + font-size: 18px; + height: 18px; + width: 18px; + } +} diff --git a/samples/chatbot/genkit-app/src/app/app.component.spec.ts b/samples/chatbot/genkit-app/src/app/app.component.spec.ts new file mode 100644 index 000000000..02dac7e71 --- /dev/null +++ b/samples/chatbot/genkit-app/src/app/app.component.spec.ts @@ -0,0 +1,47 @@ +/** + * Copyright 2024 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 { TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AppComponent], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have the 'genkit-app' title`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('genkit-app'); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain( + 'Hello, genkit-app' + ); + }); +}); diff --git a/samples/chatbot/genkit-app/src/app/app.component.ts b/samples/chatbot/genkit-app/src/app/app.component.ts new file mode 100644 index 000000000..39d4f4ea0 --- /dev/null +++ b/samples/chatbot/genkit-app/src/app/app.component.ts @@ -0,0 +1,45 @@ +/** + * Copyright 2024 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 { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTabNavPanel, MatTabsModule } from '@angular/material/tabs'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'app-root', + standalone: true, + imports: [ + CommonModule, + MatToolbarModule, + RouterOutlet, + MatIconModule, + MatTabNavPanel, + MatButtonModule, + MatTabsModule, + MatToolbarModule, + MatTooltipModule, + RouterLink, + RouterLinkActive, + ], + templateUrl: './app.component.html', + styleUrl: './app.component.scss', +}) +export class AppComponent {} diff --git a/samples/chatbot/genkit-app/src/app/app.config.ts b/samples/chatbot/genkit-app/src/app/app.config.ts new file mode 100644 index 000000000..7c47634a3 --- /dev/null +++ b/samples/chatbot/genkit-app/src/app/app.config.ts @@ -0,0 +1,31 @@ +/** + * Copyright 2024 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 { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; + +import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; +import { provideMarkdown } from 'ngx-markdown'; +import { routes } from './app.routes'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes), + provideAnimationsAsync(), + provideMarkdown(), + ], +}; diff --git a/samples/chatbot/genkit-app/src/app/app.routes.ts b/samples/chatbot/genkit-app/src/app/app.routes.ts new file mode 100644 index 000000000..d59a096c2 --- /dev/null +++ b/samples/chatbot/genkit-app/src/app/app.routes.ts @@ -0,0 +1,26 @@ +/** + * Copyright 2024 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 { Routes } from '@angular/router'; +import { ChatbotComponent } from './samples/chatbot/chatbot.component'; + +export const routes: Routes = [ + { + path: 'home', + component: ChatbotComponent, + }, + { path: '**', redirectTo: '/home' }, +]; diff --git a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.html b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.html new file mode 100644 index 000000000..782825689 --- /dev/null +++ b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.html @@ -0,0 +1,58 @@ + + +
+ @if (llmIndex === undefined) { +

Choose an LLM

+ @for (name of llmNames; track name; let index = $index) { + + } + } @else { +

Chat with {{ llmNames[llmIndex] }}

+ +
+
+ {{ entry.text }} +
+
+ + + +
+
+ + @if (error) { +
{{ error }}
+ } + +
+ + Chat input. Press Enter to submit (Shift+Enter for line + break) + + + +
+ } +
diff --git a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.scss b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.scss new file mode 100644 index 000000000..02802405d --- /dev/null +++ b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.scss @@ -0,0 +1,75 @@ +/** + * Copyright 2024 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. + */ + +.wrapper { + margin-left: auto; + margin-right: auto; + padding: 20px; + width: 800px; +} + +.user-bubble { + background-color: bisque; + border: 1px solid #ccc; + border-radius: 10px; + margin-bottom: 20px; + margin-left: auto; + margin-right: 0; + min-width: 300px; + padding: 20px; + white-space: pre-wrap; + width: 80%; +} + +.model-bubble { + border: 1px solid #ccc; + border-radius: 10px; + margin-bottom: 20px; + min-width: 300px; + padding: 20px; + width: 80%; + &.llm-0 { + background-color: rgb(218, 247, 237); + } + &.llm-1 { + background-color: rgb(244, 216, 247); + } + + .text { + white-space: pre-wrap; + } + + .model-name { + font-size: small; + font-weight: 100; + margin-bottom: 11px; + } +} + +.input-field { + min-width: 400px; + vertical-align: top; + width: 730px; +} + +.error { + background-color: pink; + border: 1px solid red; + border-radius: 15px; + margin: 20px 0; + overflow: auto; + padding: 20px; +} diff --git a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts new file mode 100644 index 000000000..c79a6e1f7 --- /dev/null +++ b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts @@ -0,0 +1,38 @@ +/** + * Copyright 2024 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 { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChatbotComponent } from './chatbot.component'; + +describe('ChatbotComponent', () => { + let component: ChatbotComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ChatbotComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ChatbotComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts new file mode 100644 index 000000000..293bdf106 --- /dev/null +++ b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts @@ -0,0 +1,157 @@ +/** + * Copyright 2024 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 { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { + FormControl, + FormsModule, + ReactiveFormsModule, + Validators, +} from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { provideNativeDateAdapter } from '@angular/material/core'; +import { MatDatepickerModule } from '@angular/material/datepicker'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatRadioModule } from '@angular/material/radio'; +import { streamFlow } from 'genkit/client'; +import { MarkdownModule } from 'ngx-markdown'; + +const url = 'http://127.0.0.1:3400/chatbotFlow'; + +interface ToolResponse { + name: string; + ref: string; + output?: unknown; +} + +interface InputSchema { + role: 'user'; + text?: string; + toolResponse?: ToolResponse; +} + +interface ToolRequest { + name: string; + ref: string; + input?: unknown; +} +interface OutputSchema { + role: 'model'; + text?: string; + toolRequest?: ToolRequest; +} + +@Component({ + selector: 'app-chatbot', + standalone: true, + providers: [provideNativeDateAdapter()], + imports: [ + CommonModule, + FormsModule, + MatFormFieldModule, + MatInputModule, + ReactiveFormsModule, + MatButtonModule, + MatIconModule, + MatProgressBarModule, + MatDatepickerModule, + MarkdownModule, + MatRadioModule, + ], + templateUrl: './chatbot.component.html', + styleUrl: './chatbot.component.scss', +}) +export class ChatbotComponent { + history: (InputSchema | OutputSchema)[] = []; + error?: string; + input?: string; + loading = false; + id = Date.now() + '' + Math.floor(Math.random() * 1000000000); + llmIndex: number | undefined; + + llmNames = ['Gemini 1.5 Flash', 'Llama 3.1 405b']; + + chatFormControl = new FormControl( + 'write a function that adds two number together', + [Validators.required] + ); + + ask(input?: string) { + const text = this.chatFormControl.value!.trim(); + if (!text) return; + this.history.push({ role: 'user', text: text }); + this.chatFormControl.setValue(''); + this.chatFormControl.disable(); + this.callFlow({ role: 'user', text }); + this.loading = true; + } + + async callFlow(input: InputSchema) { + this.error = undefined; + this.loading = true; + try { + const response = await streamFlow({ + url, + input: { + prompt: input, + conversationId: this.id, + llmIndex: this.llmIndex, + }, + }); + + let textBlock: OutputSchema | undefined = undefined; + for await (const chunk of response.stream()) { + for (const content of chunk.content) { + if (content.text) { + if (!textBlock) { + textBlock = { role: 'model', text: content.text! }; + this.history.push(textBlock); + } else { + textBlock.text += content.text!; + } + } + } + } + + this.loading = false; + this.chatFormControl.enable(); + + await response.output(); + } catch (e) { + this.loading = false; + this.chatFormControl.enable(); + if ((e as any).cause) { + this.error = `${(e as any).cause}`; + } else { + this.error = `${e}`; + } + } + } + + keyPress(event: KeyboardEvent) { + if (event.key === 'Enter') { + if (event.ctrlKey || event.shiftKey) { + this.input += '\n'; + } else { + this.ask(this.input); + } + } + } +} diff --git a/samples/chatbot/genkit-app/src/index.html b/samples/chatbot/genkit-app/src/index.html new file mode 100644 index 000000000..822a28173 --- /dev/null +++ b/samples/chatbot/genkit-app/src/index.html @@ -0,0 +1,35 @@ + + + + + + + GenkitApp + + + + + + + + + + diff --git a/samples/chatbot/genkit-app/src/main.ts b/samples/chatbot/genkit-app/src/main.ts new file mode 100644 index 000000000..b1be530a2 --- /dev/null +++ b/samples/chatbot/genkit-app/src/main.ts @@ -0,0 +1,23 @@ +/** + * Copyright 2024 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 { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err) +); diff --git a/samples/chatbot/genkit-app/src/styles.scss b/samples/chatbot/genkit-app/src/styles.scss new file mode 100644 index 000000000..0ff0b59e1 --- /dev/null +++ b/samples/chatbot/genkit-app/src/styles.scss @@ -0,0 +1,55 @@ +/** + * Copyright 2024 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. + */ + +/* You can add global styles to this file, and also import other style files */ + +:root { + --header-height: 65px; + --container-border-radius: 20px; + --input-border-radius: 8px; +} + +html, +body { + height: 100%; +} + +body { + background-color: var(--app-background); + color: var(--mat-app-text-color); + margin: 0; +} + +hr { + border-bottom: 1px solid var(--divider-color); + border-width: 0 0 1px; + margin: 12px 0; +} + +a { + color: var(--link-color); + text-decoration: none; +} + +pre { + margin: 0; + white-space: pre-wrap; +} + +// Helper for filling available space in flex layouts +.flex-spacer { + flex: 1; +} diff --git a/samples/chatbot/genkit-app/tsconfig.app.json b/samples/chatbot/genkit-app/tsconfig.app.json new file mode 100644 index 000000000..84f1f992d --- /dev/null +++ b/samples/chatbot/genkit-app/tsconfig.app.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"] +} diff --git a/samples/chatbot/genkit-app/tsconfig.json b/samples/chatbot/genkit-app/tsconfig.json new file mode 100644 index 000000000..437984834 --- /dev/null +++ b/samples/chatbot/genkit-app/tsconfig.json @@ -0,0 +1,29 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "esModuleInterop": true, + "sourceMap": true, + "declaration": false, + "experimentalDecorators": true, + "moduleResolution": "bundler", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "useDefineForClassFields": false, + "lib": ["ES2022", "dom"] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/samples/chatbot/genkit-app/tsconfig.spec.json b/samples/chatbot/genkit-app/tsconfig.spec.json new file mode 100644 index 000000000..47e3dd755 --- /dev/null +++ b/samples/chatbot/genkit-app/tsconfig.spec.json @@ -0,0 +1,9 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": ["jasmine"] + }, + "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/samples/chatbot/package.json b/samples/chatbot/package.json new file mode 100644 index 000000000..5cfb52ff9 --- /dev/null +++ b/samples/chatbot/package.json @@ -0,0 +1,17 @@ +{ + "scripts": { + "start": "concurrently npm:start:server npm:start:ng", + "setup": "npm i && cd server && npm i && cd ../genkit-app && npm i", + "start:server": "cd server && npm run genkit:dev", + "start:ng": "cd genkit-app && npm start" + }, + "name": "js-angular", + "version": "1.0.0", + "description": "This is a simple UI for streaming RPG character generator.", + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "concurrently": "^8.2.2" + } +} diff --git a/samples/chatbot/server/package.json b/samples/chatbot/server/package.json new file mode 100644 index 000000000..4f13d4997 --- /dev/null +++ b/samples/chatbot/server/package.json @@ -0,0 +1,29 @@ +{ + "main": "lib/index.js", + "scripts": { + "start": "node lib/index.js", + "genkit:dev": "genkit start -- npm run dev", + "dev": "tsx --watch src/index.ts", + "build": "tsc", + "build:watch": "tsc --watch", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "name": "js-angular", + "version": "1.0.0", + "description": "This is a simple UI for streaming RPG character generator.", + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "genkit": "^0.9.0-rc || ^0.9", + "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9", + "express": "^4.21.0", + "partial-json": "^0.1.7", + "zod": "^3.23.8" + }, + "devDependencies": { + "genkit-cli": "^0.9.0-rc || ^0.9", + "typescript": "^5.4.5", + "tsx": "^4.19.2" + } +} diff --git a/samples/chatbot/server/src/index.ts b/samples/chatbot/server/src/index.ts new file mode 100644 index 000000000..7bd2144bf --- /dev/null +++ b/samples/chatbot/server/src/index.ts @@ -0,0 +1,100 @@ +/** + * Copyright 2024 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 { gemini15Flash, vertexAI } from '@genkit-ai/vertexai'; +import { + VertexAIEvaluationMetricType, + vertexAIEvaluation, +} from '@genkit-ai/vertexai/evaluation'; +import { llama31, vertexAIModelGarden } from '@genkit-ai/vertexai/modelgarden'; +import { ModelReference, PartSchema, genkit, run } from 'genkit'; +import { GenerateResponseChunkSchema } from 'genkit/model'; +import { z } from 'zod'; +import { inMemoryStore } from './memory.js'; + +export const AgentInput = z.object({ + conversationId: z.string(), + prompt: z.union([z.string(), PartSchema, z.array(PartSchema)]), + config: z.record(z.string(), z.any()).optional(), + llmIndex: z.number(), +}); + +const ai = genkit({ + plugins: [ + vertexAI({ + location: 'us-central1', + }), + vertexAIModelGarden({ + location: 'us-central1', + models: [llama31], + }), + vertexAIEvaluation({ + location: 'us-central1', + metrics: [ + VertexAIEvaluationMetricType.SAFETY, + VertexAIEvaluationMetricType.FLUENCY, + ], + }), + ], +}); + +const llms: ModelReference[] = [gemini15Flash, llama31]; + +const historyStore = inMemoryStore(); + +export const chatbotFlow = ai.defineStreamingFlow( + { + name: 'chatbotFlow', + inputSchema: AgentInput, + outputSchema: z.string(), + streamSchema: GenerateResponseChunkSchema, + }, + async (request, streamingCallback) => { + // Retrieve conversation history. + const history = await run( + 'retrieve-history', + request.conversationId, + async () => { + return (await historyStore?.load(request.conversationId)) || []; + } + ); + + // Run the user prompt (with history) through the primary LLM. + const mainResp = await ai.generate({ + prompt: request.prompt, + messages: history, + model: llms[request.llmIndex], + streamingCallback, + }); + + // Save history. + await run( + 'save-history', + { + conversationId: request.conversationId, + history: mainResp.messages, + }, + async () => { + await historyStore?.save(request.conversationId, mainResp.messages); + } + ); + return mainResp.text; + } +); + +ai.startFlowServer({ + flows: [chatbotFlow], +}); diff --git a/samples/chatbot/server/src/memory.ts b/samples/chatbot/server/src/memory.ts new file mode 100644 index 000000000..4c3561f01 --- /dev/null +++ b/samples/chatbot/server/src/memory.ts @@ -0,0 +1,35 @@ +/** + * Copyright 2024 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 { MessageData } from 'genkit'; + +const chatHistory: Record = {}; + +export interface HistoryStore { + load(id: string): Promise; + save(id: string, history: MessageData[]): Promise; +} + +export function inMemoryStore(): HistoryStore { + return { + async load(id: string): Promise { + return chatHistory[id]; + }, + async save(id: string, history: MessageData[]) { + chatHistory[id] = history; + }, + }; +} diff --git a/samples/chatbot/server/tsconfig.json b/samples/chatbot/server/tsconfig.json new file mode 100644 index 000000000..efbb566bf --- /dev/null +++ b/samples/chatbot/server/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compileOnSave": true, + "include": ["src"], + "compilerOptions": { + "module": "commonjs", + "noImplicitReturns": true, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017", + "skipLibCheck": true, + "esModuleInterop": true + } +} diff --git a/samples/js-angular/.gitignore b/samples/js-angular/.gitignore new file mode 100644 index 000000000..7951405f8 --- /dev/null +++ b/samples/js-angular/.gitignore @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/samples/js-angular/README.md b/samples/js-angular/README.md new file mode 100644 index 000000000..4e02b6f32 --- /dev/null +++ b/samples/js-angular/README.md @@ -0,0 +1,24 @@ +# Angular and Genkit streaming sample + +This is a simple UI for streaming RPG character generator. + +To build: + +```bash +npm i +npm run build +``` + +The sample is using Vertex AI, so you'll need to auth: + +```bash +gcloud auth application-default login +``` + +To run the sample: + +```bash +npm start +``` + +Point your browser to http://localhost:4200/ diff --git a/samples/js-angular/genkit-app/.editorconfig b/samples/js-angular/genkit-app/.editorconfig new file mode 100644 index 000000000..59d9a3a3e --- /dev/null +++ b/samples/js-angular/genkit-app/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/samples/js-angular/genkit-app/.gitignore b/samples/js-angular/genkit-app/.gitignore new file mode 100644 index 000000000..cc7b14135 --- /dev/null +++ b/samples/js-angular/genkit-app/.gitignore @@ -0,0 +1,42 @@ +# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files +.DS_Store +Thumbs.db diff --git a/samples/js-angular/genkit-app/README.md b/samples/js-angular/genkit-app/README.md new file mode 100644 index 000000000..0aeb2095b --- /dev/null +++ b/samples/js-angular/genkit-app/README.md @@ -0,0 +1,27 @@ +# GenkitApp + +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 18.0.2. + +## Development server + +Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. + +## Code scaffolding + +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. + +## Build + +Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Running unit tests + +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Running end-to-end tests + +Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/samples/js-angular/genkit-app/angular.json b/samples/js-angular/genkit-app/angular.json new file mode 100644 index 000000000..8762ae48e --- /dev/null +++ b/samples/js-angular/genkit-app/angular.json @@ -0,0 +1,99 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "genkit-app": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:application", + "options": { + "outputPath": "dist/genkit-app", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "@angular/material/prebuilt-themes/azure-blue.css", + "src/styles.scss" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kB", + "maximumError": "1MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kB", + "maximumError": "4kB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "genkit-app:build:production" + }, + "development": { + "buildTarget": "genkit-app:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": ["zone.js", "zone.js/testing"], + "tsConfig": "tsconfig.spec.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "@angular/material/prebuilt-themes/azure-blue.css", + "src/styles.scss" + ], + "scripts": [] + } + } + } + } + } +} diff --git a/samples/js-angular/genkit-app/package.json b/samples/js-angular/genkit-app/package.json new file mode 100644 index 000000000..89cd74b7f --- /dev/null +++ b/samples/js-angular/genkit-app/package.json @@ -0,0 +1,41 @@ +{ + "name": "genkit-app", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "private": true, + "dependencies": { + "@angular/animations": "^18.0.0", + "@angular/cdk": "^18.0.1", + "@angular/common": "^18.0.0", + "@angular/compiler": "^18.0.0", + "@angular/core": "^18.0.0", + "@angular/forms": "^18.0.0", + "@angular/material": "^18.0.1", + "@angular/platform-browser": "^18.0.0", + "@angular/platform-browser-dynamic": "^18.0.0", + "@angular/router": "^18.0.0", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.14.3", + "genkit": "^0.9.0-rc || ^0.9" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^18.0.2", + "@angular/cli": "^18.0.2", + "@angular/compiler-cli": "^18.0.0", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.1.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.4.2" + } +} diff --git a/samples/js-angular/genkit-app/public/favicon.ico b/samples/js-angular/genkit-app/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..57614f9c967596fad0a3989bec2b1deff33034f6 GIT binary patch literal 15086 zcmd^G33O9Omi+`8$@{|M-I6TH3wzF-p5CV8o}7f~KxR60LK+ApEFB<$bcciv%@SmA zV{n>g85YMFFeU*Uvl=i4v)C*qgnb;$GQ=3XTe9{Y%c`mO%su)noNCCQ*@t1WXn|B(hQ7i~ zrUK8|pUkD6#lNo!bt$6)jR!&C?`P5G(`e((P($RaLeq+o0Vd~f11;qB05kdbAOm?r zXv~GYr_sibQO9NGTCdT;+G(!{4Xs@4fPak8#L8PjgJwcs-Mm#nR_Z0s&u?nDX5^~@ z+A6?}g0|=4e_LoE69pPFO`yCD@BCjgKpzMH0O4Xs{Ahc?K3HC5;l=f zg>}alhBXX&);z$E-wai+9TTRtBX-bWYY@cl$@YN#gMd~tM_5lj6W%8ah4;uZ;jP@Q zVbuel1rPA?2@x9Y+u?e`l{Z4ngfG5q5BLH5QsEu4GVpt{KIp1?U)=3+KQ;%7ec8l* zdV=zZgN5>O3G(3L2fqj3;oBbZZw$Ij@`Juz@?+yy#OPw)>#wsTewVgTK9BGt5AbZ&?K&B3GVF&yu?@(Xj3fR3n+ZP0%+wo)D9_xp>Z$`A4 zfV>}NWjO#3lqumR0`gvnffd9Ka}JJMuHS&|55-*mCD#8e^anA<+sFZVaJe7{=p*oX zE_Uv?1>e~ga=seYzh{9P+n5<+7&9}&(kwqSaz;1aD|YM3HBiy<))4~QJSIryyqp| z8nGc(8>3(_nEI4n)n7j(&d4idW1tVLjZ7QbNLXg;LB ziHsS5pXHEjGJZb59KcvS~wv;uZR-+4qEqow`;JCfB*+b^UL^3!?;-^F%yt=VjU|v z39SSqKcRu_NVvz!zJzL0CceJaS6%!(eMshPv_0U5G`~!a#I$qI5Ic(>IONej@aH=f z)($TAT#1I{iCS4f{D2+ApS=$3E7}5=+y(rA9mM#;Cky%b*Gi0KfFA`ofKTzu`AV-9 znW|y@19rrZ*!N2AvDi<_ZeR3O2R{#dh1#3-d%$k${Rx42h+i&GZo5!C^dSL34*AKp z27mTd>k>?V&X;Nl%GZ(>0s`1UN~Hfyj>KPjtnc|)xM@{H_B9rNr~LuH`Gr5_am&Ep zTjZA8hljNj5H1Ipm-uD9rC}U{-vR!eay5&6x6FkfupdpT*84MVwGpdd(}ib)zZ3Ky z7C$pnjc82(W_y_F{PhYj?o!@3__UUvpX)v69aBSzYj3 zdi}YQkKs^SyXyFG2LTRz9{(w}y~!`{EuAaUr6G1M{*%c+kP1olW9z23dSH!G4_HSK zzae-DF$OGR{ofP*!$a(r^5Go>I3SObVI6FLY)N@o<*gl0&kLo-OT{Tl*7nCz>Iq=? zcigIDHtj|H;6sR?or8Wd_a4996GI*CXGU}o;D9`^FM!AT1pBY~?|4h^61BY#_yIfO zKO?E0 zJ{Pc`9rVEI&$xxXu`<5E)&+m(7zX^v0rqofLs&bnQT(1baQkAr^kEsk)15vlzAZ-l z@OO9RF<+IiJ*O@HE256gCt!bF=NM*vh|WVWmjVawcNoksRTMvR03H{p@cjwKh(CL4 z7_PB(dM=kO)!s4fW!1p0f93YN@?ZSG` z$B!JaAJCtW$B97}HNO9(x-t30&E}Mo1UPi@Av%uHj~?T|!4JLwV;KCx8xO#b9IlUW zI6+{a@Wj|<2Y=U;a@vXbxqZNngH8^}LleE_4*0&O7#3iGxfJ%Id>+sb;7{L=aIic8 z|EW|{{S)J-wr@;3PmlxRXU8!e2gm_%s|ReH!reFcY8%$Hl4M5>;6^UDUUae?kOy#h zk~6Ee_@ZAn48Bab__^bNmQ~+k=02jz)e0d9Z3>G?RGG!65?d1>9}7iG17?P*=GUV-#SbLRw)Hu{zx*azHxWkGNTWl@HeWjA?39Ia|sCi{e;!^`1Oec zb>Z|b65OM*;eC=ZLSy?_fg$&^2xI>qSLA2G*$nA3GEnp3$N-)46`|36m*sc#4%C|h zBN<2U;7k>&G_wL4=Ve5z`ubVD&*Hxi)r@{4RCDw7U_D`lbC(9&pG5C*z#W>8>HU)h z!h3g?2UL&sS!oY5$3?VlA0Me9W5e~V;2jds*fz^updz#AJ%G8w2V}AEE?E^=MK%Xt z__Bx1cr7+DQmuHmzn*|hh%~eEc9@m05@clWfpEFcr+06%0&dZJH&@8^&@*$qR@}o3 z@Tuuh2FsLz^zH+dN&T&?0G3I?MpmYJ;GP$J!EzjeM#YLJ!W$}MVNb0^HfOA>5Fe~UNn%Zk(PT@~9}1dt)1UQ zU*B5K?Dl#G74qmg|2>^>0WtLX#Jz{lO4NT`NYB*(L#D|5IpXr9v&7a@YsGp3vLR7L zHYGHZg7{ie6n~2p$6Yz>=^cEg7tEgk-1YRl%-s7^cbqFb(U7&Dp78+&ut5!Tn(hER z|Gp4Ed@CnOPeAe|N>U(dB;SZ?NU^AzoD^UAH_vamp6Ws}{|mSq`^+VP1g~2B{%N-!mWz<`)G)>V-<`9`L4?3dM%Qh6<@kba+m`JS{Ya@9Fq*m6$$ zA1%Ogc~VRH33|S9l%CNb4zM%k^EIpqY}@h{w(aBcJ9c05oiZx#SK9t->5lSI`=&l~ z+-Ic)a{FbBhXV$Xt!WRd`R#Jk-$+_Z52rS>?Vpt2IK<84|E-SBEoIw>cs=a{BlQ7O z-?{Fy_M&84&9|KM5wt~)*!~i~E=(6m8(uCO)I=)M?)&sRbzH$9Rovzd?ZEY}GqX+~ zFbEbLz`BZ49=2Yh-|<`waK-_4!7`ro@zlC|r&I4fc4oyb+m=|c8)8%tZ-z5FwhzDt zL5kB@u53`d@%nHl0Sp)Dw`(QU&>vujEn?GPEXUW!Wi<+4e%BORl&BIH+SwRcbS}X@ z01Pk|vA%OdJKAs17zSXtO55k!;%m9>1eW9LnyAX4uj7@${O6cfii`49qTNItzny5J zH&Gj`e}o}?xjQ}r?LrI%FjUd@xflT3|7LA|ka%Q3i}a8gVm<`HIWoJGH=$EGClX^C0lysQJ>UO(q&;`T#8txuoQ_{l^kEV9CAdXuU1Ghg8 zN_6hHFuy&1x24q5-(Z7;!poYdt*`UTdrQOIQ!2O7_+AHV2hgXaEz7)>$LEdG z<8vE^Tw$|YwZHZDPM!SNOAWG$?J)MdmEk{U!!$M#fp7*Wo}jJ$Q(=8>R`Ats?e|VU?Zt7Cdh%AdnfyN3MBWw{ z$OnREvPf7%z6`#2##_7id|H%Y{vV^vWXb?5d5?a_y&t3@p9t$ncHj-NBdo&X{wrfJ zamN)VMYROYh_SvjJ=Xd!Ga?PY_$;*L=SxFte!4O6%0HEh%iZ4=gvns7IWIyJHa|hT z2;1+e)`TvbNb3-0z&DD_)Jomsg-7p_Uh`wjGnU1urmv1_oVqRg#=C?e?!7DgtqojU zWoAB($&53;TsXu^@2;8M`#z{=rPy?JqgYM0CDf4v@z=ZD|ItJ&8%_7A#K?S{wjxgd z?xA6JdJojrWpB7fr2p_MSsU4(R7=XGS0+Eg#xR=j>`H@R9{XjwBmqAiOxOL` zt?XK-iTEOWV}f>Pz3H-s*>W z4~8C&Xq25UQ^xH6H9kY_RM1$ch+%YLF72AA7^b{~VNTG}Tj#qZltz5Q=qxR`&oIlW Nr__JTFzvMr^FKp4S3v*( literal 0 HcmV?d00001 diff --git a/samples/js-angular/genkit-app/src/app/app.component.html b/samples/js-angular/genkit-app/src/app/app.component.html new file mode 100644 index 000000000..0165f5b0f --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/app.component.html @@ -0,0 +1,36 @@ + + + diff --git a/samples/js-angular/genkit-app/src/app/app.component.scss b/samples/js-angular/genkit-app/src/app/app.component.scss new file mode 100644 index 000000000..0e0b80557 --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/app.component.scss @@ -0,0 +1,67 @@ +/** + * Copyright 2024 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. + */ + +app-bar { + border-bottom: 1px solid var(--divider-color); + grid-area: header; +} + +article { + grid-area: content; +} + +.wrapper { + display: grid; + grid-template: + 'header' auto + 'content' 1fr + / 1fr; + height: 100vh; +} + +.home-link { + align-items: center; + color: var(--mat-app-color); + display: flex; + gap: 8px; + + img { + height: 22px; + padding-left: 4px; + } +} + +.mat-toolbar { + background: #d7e3ff; + color: #005cbb; + gap: 4px; +} + +nav { + --mdc-secondary-navigation-tab-container-height: 64px; + --mat-tab-header-divider-height: 0; + margin-left: 32px; +} + +.preview-badge { + margin-left: 8px; + + mat-icon { + font-size: 18px; + height: 18px; + width: 18px; + } +} diff --git a/samples/js-angular/genkit-app/src/app/app.component.spec.ts b/samples/js-angular/genkit-app/src/app/app.component.spec.ts new file mode 100644 index 000000000..02dac7e71 --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/app.component.spec.ts @@ -0,0 +1,47 @@ +/** + * Copyright 2024 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 { TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AppComponent], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have the 'genkit-app' title`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('genkit-app'); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain( + 'Hello, genkit-app' + ); + }); +}); diff --git a/samples/js-angular/genkit-app/src/app/app.component.ts b/samples/js-angular/genkit-app/src/app/app.component.ts new file mode 100644 index 000000000..39d4f4ea0 --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/app.component.ts @@ -0,0 +1,45 @@ +/** + * Copyright 2024 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 { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTabNavPanel, MatTabsModule } from '@angular/material/tabs'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; + +@Component({ + selector: 'app-root', + standalone: true, + imports: [ + CommonModule, + MatToolbarModule, + RouterOutlet, + MatIconModule, + MatTabNavPanel, + MatButtonModule, + MatTabsModule, + MatToolbarModule, + MatTooltipModule, + RouterLink, + RouterLinkActive, + ], + templateUrl: './app.component.html', + styleUrl: './app.component.scss', +}) +export class AppComponent {} diff --git a/samples/js-angular/genkit-app/src/app/app.config.ts b/samples/js-angular/genkit-app/src/app/app.config.ts new file mode 100644 index 000000000..3d04dfa9c --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/app.config.ts @@ -0,0 +1,29 @@ +/** + * Copyright 2024 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 { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; + +import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; +import { routes } from './app.routes'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes), + provideAnimationsAsync(), + ], +}; diff --git a/samples/js-angular/genkit-app/src/app/app.routes.ts b/samples/js-angular/genkit-app/src/app/app.routes.ts new file mode 100644 index 000000000..bd70f607f --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/app.routes.ts @@ -0,0 +1,36 @@ +/** + * Copyright 2024 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 { Routes } from '@angular/router'; +import { HomeComponent } from './home/home.component'; +import { ChatbotComponent } from './samples/chatbot/chatbot.component'; +import { StreamingJSONComponent } from './samples/streaming-json/streaming-json.component'; + +export const routes: Routes = [ + { + path: 'home', + component: HomeComponent, + }, + { + path: 'samples/streaming-json', + component: StreamingJSONComponent, + }, + { + path: 'samples/chatbot', + component: ChatbotComponent, + }, + { path: '**', redirectTo: '/home' }, +]; diff --git a/samples/js-angular/genkit-app/src/app/home/home.component.html b/samples/js-angular/genkit-app/src/app/home/home.component.html new file mode 100644 index 000000000..f57987dcc --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/home/home.component.html @@ -0,0 +1,32 @@ + + +
+

Samples

+ + + +
diff --git a/samples/js-angular/genkit-app/src/app/home/home.component.scss b/samples/js-angular/genkit-app/src/app/home/home.component.scss new file mode 100644 index 000000000..da80fa8b3 --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/home/home.component.scss @@ -0,0 +1,19 @@ +/** + * Copyright 2024 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. + */ + +.wrapper { + padding: 20px; +} diff --git a/samples/js-angular/genkit-app/src/app/home/home.component.spec.ts b/samples/js-angular/genkit-app/src/app/home/home.component.spec.ts new file mode 100644 index 000000000..19eda49ae --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/home/home.component.spec.ts @@ -0,0 +1,38 @@ +/** + * Copyright 2024 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 { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; + +describe('HomeComponent', () => { + let component: HomeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [HomeComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/samples/js-angular/genkit-app/src/app/home/home.component.ts b/samples/js-angular/genkit-app/src/app/home/home.component.ts new file mode 100644 index 000000000..f1e1997c1 --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/home/home.component.ts @@ -0,0 +1,27 @@ +/** + * Copyright 2024 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 { Component } from '@angular/core'; +import { RouterLink, RouterLinkActive } from '@angular/router'; + +@Component({ + selector: 'app-home', + standalone: true, + imports: [RouterLink, RouterLinkActive], + templateUrl: './home.component.html', + styleUrl: './home.component.scss', +}) +export class HomeComponent {} diff --git a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.html b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.html new file mode 100644 index 000000000..00829ec6d --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.html @@ -0,0 +1,77 @@ + + +
+

Chat with Agent Smith

+ +
+
+ {{ entry.text }} +
+
+ {{ entry.text }} +
+
+
+

+ sunny_snowing + {{ getWeatherLocation(entry.toolRequest) }} 27°C +

+
Warn sunny day with a mix of sun and snow.
+
+
+ + Choose a date + + MM/DD/YYYY + + + + + + + + +
+
+ Oops... unknown tool {{ entry.toolRequest.name }} +
+
+
+
+
+ +
+ + Chat input + + + + +
+
diff --git a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.scss b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.scss new file mode 100644 index 000000000..da62c1c12 --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.scss @@ -0,0 +1,55 @@ +/** + * Copyright 2024 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. + */ + +.wrapper { + margin-left: auto; + margin-right: auto; + padding: 20px; + width: 800px; +} + +.user-bubble { + background-color: #eeddee; + border: 1px solid #ccc; + border-radius: 10px; + margin-bottom: 20px; + margin-left: auto; + margin-right: 0; + min-width: 300px; + padding: 20px; + white-space: pre-wrap; + width: 80%; +} + +.model-bubble { + background-color: #ddddee; + border: 1px solid #ccc; + border-radius: 10px; + margin-bottom: 20px; + min-width: 300px; + padding: 20px; + width: 80%; + + .text { + white-space: pre-wrap; + } +} + +.input-field { + min-width: 400px; + vertical-align: top; + width: 730px; +} diff --git a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts new file mode 100644 index 000000000..c79a6e1f7 --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts @@ -0,0 +1,38 @@ +/** + * Copyright 2024 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 { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChatbotComponent } from './chatbot.component'; + +describe('ChatbotComponent', () => { + let component: ChatbotComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ChatbotComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ChatbotComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts new file mode 100644 index 000000000..98e454219 --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts @@ -0,0 +1,158 @@ +/** + * Copyright 2024 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 { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { + FormControl, + FormsModule, + ReactiveFormsModule, + Validators, +} from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { provideNativeDateAdapter } from '@angular/material/core'; +import { + MatDatepickerInputEvent, + MatDatepickerModule, +} from '@angular/material/datepicker'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { streamFlow } from 'genkit/client'; + +const url = 'http://127.0.0.1:3400/chatbotFlow'; + +interface ToolResponse { + name: string; + ref: string; + output?: unknown; +} + +interface InputSchema { + role: 'user'; + text?: string; + toolResponse?: ToolResponse; +} + +interface ToolRequest { + name: string; + ref: string; + input?: unknown; +} +interface OutputSchema { + role: 'model'; + text?: string; + toolRequest?: ToolRequest; +} + +@Component({ + selector: 'app-chatbot', + standalone: true, + providers: [provideNativeDateAdapter()], + imports: [ + CommonModule, + FormsModule, + MatFormFieldModule, + MatInputModule, + ReactiveFormsModule, + MatButtonModule, + MatIconModule, + MatProgressBarModule, + MatDatepickerModule, + ], + templateUrl: './chatbot.component.html', + styleUrl: './chatbot.component.scss', +}) +export class ChatbotComponent { + history: (InputSchema | OutputSchema)[] = []; + error?: string; + input?: string; + loading = false; + id = Date.now() + '' + Math.floor(Math.random() * 1000000000); + + chatFormControl = new FormControl('', [Validators.required]); + + ask(input?: string) { + const text = this.chatFormControl.value!.trim(); + if (!text) return; + this.history.push({ role: 'user', text: text }); + this.chatFormControl.setValue(''); + this.chatFormControl.disable(); + this.callFlow({ role: 'user', text }); + this.loading = true; + } + + async callFlow(input: InputSchema) { + this.error = undefined; + this.loading = true; + try { + const response = await streamFlow({ + url, + input: { + prompt: input, + conversationId: this.id, + }, + }); + + let textBlock: OutputSchema | undefined = undefined; + for await (const chunk of response.stream()) { + for (const content of chunk.content) { + if (content.text) { + if (!textBlock) { + textBlock = { role: 'model', text: content.text! }; + this.history.push(textBlock); + } else { + textBlock.text += content.text!; + } + } + if (content.toolRequest) { + this.history.push({ + role: 'model', + toolRequest: content.toolRequest, + }); + } + } + } + + this.loading = false; + this.chatFormControl.enable(); + } catch (e) { + this.loading = false; + this.chatFormControl.enable(); + if ((e as any).cause) { + this.error = `${(e as any).cause}`; + } else { + this.error = `${e}`; + } + } + } + + getWeatherLocation(toolRequest: ToolRequest) { + return (toolRequest.input as any).location; + } + + datePicked(toolRequest: ToolRequest, event: MatDatepickerInputEvent) { + this.callFlow({ + role: 'user', + toolResponse: { + name: toolRequest.name, + ref: toolRequest.ref, + output: `${event.value}`, + }, + }); + } +} diff --git a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.html b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.html new file mode 100644 index 000000000..e32ce2ae0 --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.html @@ -0,0 +1,36 @@ + + +
+

Stream JSON from LLM

+ This is a Game Character Generator.
+ How many game chatacters do you need? + + + +
Loading...
+
+ {{ error }} +
+
+
+ {{ character.name }} +
    +
  • {{ ability }}
  • +
+
+
+
diff --git a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.scss b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.scss new file mode 100644 index 000000000..70fcce28e --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.scss @@ -0,0 +1,23 @@ +/** + * Copyright 2024 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. + */ + +.wrapper { + padding: 20px; +} + +.characters { + margin-top: 20px; +} diff --git a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.spec.ts b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.spec.ts new file mode 100644 index 000000000..d85373105 --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.spec.ts @@ -0,0 +1,38 @@ +/** + * Copyright 2024 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 { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StreamingJSONComponent } from './streaming-json.component'; + +describe('StreamingJSONComponent', () => { + let component: StreamingJSONComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [StreamingJSONComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(StreamingJSONComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts new file mode 100644 index 000000000..9d39d3148 --- /dev/null +++ b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts @@ -0,0 +1,61 @@ +/** + * Copyright 2024 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 { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { streamFlow } from 'genkit/client'; + +const url = 'http://127.0.0.1:3400/streamCharacters'; + +@Component({ + selector: 'app-streaming-json', + standalone: true, + imports: [FormsModule, CommonModule, MatButtonModule], + templateUrl: './streaming-json.component.html', + styleUrl: './streaming-json.component.scss', +}) +export class StreamingJSONComponent { + count: string = '3'; + characters: any = undefined; + error?: string = undefined; + loading: boolean = false; + + async callFlow() { + this.characters = undefined; + this.error = undefined; + this.loading = true; + try { + const response = streamFlow({ + url, + input: parseInt(this.count), + }); + for await (const chunk of response.stream()) { + this.characters = chunk; + } + console.log('streamConsumer done', await response.output()); + this.loading = false; + } catch (e) { + this.loading = false; + if ((e as any).cause) { + this.error = `${(e as any).cause}`; + } else { + this.error = `${e}`; + } + } + } +} diff --git a/samples/js-angular/genkit-app/src/index.html b/samples/js-angular/genkit-app/src/index.html new file mode 100644 index 000000000..822a28173 --- /dev/null +++ b/samples/js-angular/genkit-app/src/index.html @@ -0,0 +1,35 @@ + + + + + + + GenkitApp + + + + + + + + + + diff --git a/samples/js-angular/genkit-app/src/main.ts b/samples/js-angular/genkit-app/src/main.ts new file mode 100644 index 000000000..b1be530a2 --- /dev/null +++ b/samples/js-angular/genkit-app/src/main.ts @@ -0,0 +1,23 @@ +/** + * Copyright 2024 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 { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err) +); diff --git a/samples/js-angular/genkit-app/src/styles.scss b/samples/js-angular/genkit-app/src/styles.scss new file mode 100644 index 000000000..0ff0b59e1 --- /dev/null +++ b/samples/js-angular/genkit-app/src/styles.scss @@ -0,0 +1,55 @@ +/** + * Copyright 2024 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. + */ + +/* You can add global styles to this file, and also import other style files */ + +:root { + --header-height: 65px; + --container-border-radius: 20px; + --input-border-radius: 8px; +} + +html, +body { + height: 100%; +} + +body { + background-color: var(--app-background); + color: var(--mat-app-text-color); + margin: 0; +} + +hr { + border-bottom: 1px solid var(--divider-color); + border-width: 0 0 1px; + margin: 12px 0; +} + +a { + color: var(--link-color); + text-decoration: none; +} + +pre { + margin: 0; + white-space: pre-wrap; +} + +// Helper for filling available space in flex layouts +.flex-spacer { + flex: 1; +} diff --git a/samples/js-angular/genkit-app/tsconfig.app.json b/samples/js-angular/genkit-app/tsconfig.app.json new file mode 100644 index 000000000..84f1f992d --- /dev/null +++ b/samples/js-angular/genkit-app/tsconfig.app.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"] +} diff --git a/samples/js-angular/genkit-app/tsconfig.json b/samples/js-angular/genkit-app/tsconfig.json new file mode 100644 index 000000000..437984834 --- /dev/null +++ b/samples/js-angular/genkit-app/tsconfig.json @@ -0,0 +1,29 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "esModuleInterop": true, + "sourceMap": true, + "declaration": false, + "experimentalDecorators": true, + "moduleResolution": "bundler", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "useDefineForClassFields": false, + "lib": ["ES2022", "dom"] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/samples/js-angular/genkit-app/tsconfig.spec.json b/samples/js-angular/genkit-app/tsconfig.spec.json new file mode 100644 index 000000000..47e3dd755 --- /dev/null +++ b/samples/js-angular/genkit-app/tsconfig.spec.json @@ -0,0 +1,9 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": ["jasmine"] + }, + "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/samples/js-angular/package.json b/samples/js-angular/package.json new file mode 100644 index 000000000..07f36dae4 --- /dev/null +++ b/samples/js-angular/package.json @@ -0,0 +1,17 @@ +{ + "name": "js-angular", + "version": "1.0.0", + "description": "This is a simple UI for streaming RPG character generator.", + "scripts": { + "start": "concurrently npm:start:server npm:start:ng", + "start:server": "cd server && npm run genkit:dev", + "start:ng": "cd genkit-app && npm run start", + "setup": "npm i && cd server && npm i && cd ../genkit-app && npm i" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "concurrently": "^8.2.2" + } +} diff --git a/samples/js-angular/server/package.json b/samples/js-angular/server/package.json new file mode 100644 index 000000000..428c00d1c --- /dev/null +++ b/samples/js-angular/server/package.json @@ -0,0 +1,27 @@ +{ + "main": "lib/index.js", + "scripts": { + "start": "node lib/index.js", + "genkit:dev": "genkit start -- tsx --watch src/index.ts", + "build": "tsc", + "build:watch": "tsc --watch", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "name": "js-angular", + "version": "1.0.0", + "description": "This is a simple UI for streaming RPG character generator.", + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "genkit": "^0.9.0-rc || ^0.9", + "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9", + "express": "^4.21.0", + "partial-json": "^0.1.7" + }, + "devDependencies": { + "genkit-cli": "^0.9.0-rc || ^0.9", + "typescript": "^5.4.5", + "tsx": "^4.19.2" + } +} diff --git a/samples/js-angular/server/src/agent.ts b/samples/js-angular/server/src/agent.ts new file mode 100644 index 000000000..3ee2f03b5 --- /dev/null +++ b/samples/js-angular/server/src/agent.ts @@ -0,0 +1,111 @@ +/** + * Copyright 2024 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 { + GenerateResponse, + Genkit, + MessageData, + ModelArgument, + PartSchema, + ToolArgument, + run, + z, +} from 'genkit'; +import { GenerateResponseSchema } from 'genkit/model'; + +export interface HistoryStore { + load(id: string): Promise; + save(id: string, history: MessageData[]): Promise; +} + +export const AgentInput = z.object({ + conversationId: z.string(), + prompt: z.union([z.string(), PartSchema, z.array(PartSchema)]), + config: z.record(z.string(), z.any()).optional(), +}); + +type AgentFn = ( + request: z.infer, + history: MessageData[] | undefined +) => Promise>; + +export function defineAgent( + ai: Genkit, + { + name, + tools, + model, + historyStore, + systemPrompt, + returnToolRequests, + }: { + name: string; + systemPrompt?: string; + tools?: ToolArgument[]; + model: ModelArgument; + historyStore?: HistoryStore; + returnToolRequests?: boolean; + }, + customFn?: AgentFn +) { + return ai.defineFlow( + { name, inputSchema: AgentInput, outputSchema: GenerateResponseSchema }, + async (request, streamingCallback) => { + const history = await run( + 'retrieve-history', + request.conversationId, + async () => { + let history = request.conversationId + ? await historyStore?.load(request.conversationId) + : undefined; + if (!history && systemPrompt) { + history = [ + { + role: 'system', + content: [ + { + text: systemPrompt, + }, + ], + }, + ]; + } + return history; + } + ); + const resp = customFn + ? await customFn(request, history) + : await ai.generate({ + prompt: request.prompt, + messages: history, + model, + tools, + returnToolRequests, + streamingCallback, + }); + await run( + 'save-history', + { conversationId: request.conversationId, history: resp.messages }, + async () => { + request.conversationId + ? await historyStore?.save(request.conversationId, resp.messages) + : undefined; + } + ); + return resp.toJSON(); + } + ); +} diff --git a/samples/js-angular/server/src/chatbot.ts b/samples/js-angular/server/src/chatbot.ts new file mode 100644 index 000000000..14f661406 --- /dev/null +++ b/samples/js-angular/server/src/chatbot.ts @@ -0,0 +1,75 @@ +/** + * Copyright 2024 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 { MessageData } from '@genkit-ai/ai/model'; +import { gemini15Flash } from '@genkit-ai/vertexai'; +import { z } from 'zod'; +import { defineAgent, HistoryStore } from './agent.js'; +import { ai } from './genkit.js'; + +const weatherTool = ai.defineTool( + { + name: 'weatherTool', + description: 'use this tool to display weather', + inputSchema: z.object({ + date: z + .string() + .describe('date (use datePicker tool if user did not specify)'), + location: z.string().describe('location (ZIP, city, etc.)'), + }), + outputSchema: z.string().optional(), + }, + async () => undefined +); + +const datePicker = ai.defineTool( + { + name: 'datePicker', + description: + 'user can use this UI tool to enter a date (prefer this over asking the user to enter the date manually)', + inputSchema: z.object({ + ignore: z.string().describe('ignore this (set to undefined)').optional(), + }), + outputSchema: z.string().optional(), + }, + async () => undefined +); + +export const chatbotFlow = defineAgent(ai, { + name: 'chatbotFlow', + model: gemini15Flash, + tools: [weatherTool, datePicker], + returnToolRequests: true, + systemPrompt: + 'You are a helpful agent. You have the personality of Agent Smith from Matrix. ' + + 'There are tools/functions at your disposal, ' + + 'feel free to call them. If you think a tool/function can help but you do ' + + 'not have sufficient context make sure to ask clarifying questions.', + historyStore: inMemoryStore(), +}); + +const chatHistory: Record = {}; + +function inMemoryStore(): HistoryStore { + return { + async load(id: string): Promise { + return chatHistory[id]; + }, + async save(id: string, history: MessageData[]) { + chatHistory[id] = history; + }, + }; +} diff --git a/samples/js-angular/server/src/genkit.ts b/samples/js-angular/server/src/genkit.ts new file mode 100644 index 000000000..5da87fb17 --- /dev/null +++ b/samples/js-angular/server/src/genkit.ts @@ -0,0 +1,22 @@ +/** + * Copyright 2024 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 vertexAI from '@genkit-ai/vertexai'; +import { genkit } from 'genkit'; + +export const ai = genkit({ + plugins: [vertexAI()], +}); diff --git a/samples/js-angular/server/src/index.ts b/samples/js-angular/server/src/index.ts new file mode 100644 index 000000000..e5b080060 --- /dev/null +++ b/samples/js-angular/server/src/index.ts @@ -0,0 +1,23 @@ +/** + * Copyright 2024 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 { chatbotFlow } from './chatbot.js'; +import { ai } from './genkit.js'; +import { streamCharacters } from './jsonStreaming.js'; + +ai.startFlowServer({ + flows: [chatbotFlow, streamCharacters], +}); diff --git a/samples/js-angular/server/src/jsonStreaming.ts b/samples/js-angular/server/src/jsonStreaming.ts new file mode 100644 index 000000000..df0611100 --- /dev/null +++ b/samples/js-angular/server/src/jsonStreaming.ts @@ -0,0 +1,80 @@ +/** + * Copyright 2024 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 { gemini15Flash } from '@genkit-ai/vertexai'; +import { z } from 'genkit'; +import { Allow, parse } from 'partial-json'; +import { ai } from './genkit.js'; + +const GameCharactersSchema = z.object({ + characters: z + .array( + z + .object({ + name: z.string().describe('Name of a character'), + abilities: z + .array(z.string()) + .describe('Various abilities (strength, magic, archery, etc.)'), + }) + .describe('Game character') + ) + .describe('Characters'), +}); + +export const streamCharacters = ai.defineStreamingFlow( + { + name: 'streamCharacters', + inputSchema: z.number(), + outputSchema: z.string(), + streamSchema: GameCharactersSchema, + }, + async (count, streamingCallback) => { + if (!streamingCallback) { + throw new Error('this flow only works in streaming mode'); + } + + const { response, stream } = await ai.generateStream({ + model: gemini15Flash, + output: { + format: 'json', + schema: GameCharactersSchema, + }, + config: { + temperature: 1, + }, + prompt: `Respond as JSON only. Generate ${count} different RPG game characters.`, + }); + + let buffer = ''; + for await (const chunk of stream) { + buffer += chunk.content[0].text!; + if (buffer.length > 10) { + streamingCallback(parse(maybeStripMarkdown(buffer), Allow.ALL)); + } + } + + return (await response).text; + } +); + +const markdownRegex = /^\s*(```json)?((.|\n)*?)(```)?\s*$/i; +function maybeStripMarkdown(withMarkdown: string) { + const mdMatch = markdownRegex.exec(withMarkdown); + if (!mdMatch) { + return withMarkdown; + } + return mdMatch[2]; +} diff --git a/samples/js-angular/server/tsconfig.json b/samples/js-angular/server/tsconfig.json new file mode 100644 index 000000000..efbb566bf --- /dev/null +++ b/samples/js-angular/server/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compileOnSave": true, + "include": ["src"], + "compilerOptions": { + "module": "commonjs", + "noImplicitReturns": true, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017", + "skipLibCheck": true, + "esModuleInterop": true + } +} diff --git a/samples/js-coffee-shop/README.md b/samples/js-coffee-shop/README.md new file mode 100644 index 000000000..618a80f5e --- /dev/null +++ b/samples/js-coffee-shop/README.md @@ -0,0 +1,6 @@ +## Running the sample + +```bash +npm i +genkit start +``` diff --git a/samples/js-coffee-shop/package.json b/samples/js-coffee-shop/package.json new file mode 100644 index 000000000..b32e5b1b5 --- /dev/null +++ b/samples/js-coffee-shop/package.json @@ -0,0 +1,35 @@ +{ + "name": "coffee-shop", + "version": "1.0.0", + "description": "Genkit samples for a coffeeshop", + "main": "lib/index.js", + "scripts": { + "start": "node lib/index.js", + "genkit:dev": "genkit start -- tsx --watch src/index.ts", + "compile": "tsc", + "build": "npm run build:clean && npm run compile", + "build:clean": "rimraf ./lib", + "build:watch": "tsc --watch", + "build-and-run": "npm run build && node lib/index.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "genkit": "^0.9.0-rc || ^0.9", + "genkitx-chromadb": "^0.9.0-rc || ^0.9", + "@genkit-ai/dev-local-vectorstore": "^0.9.0-rc || ^0.9", + "@genkit-ai/firebase": "^0.9.0-rc || ^0.9", + "@genkit-ai/googleai": "^0.9.0-rc || ^0.9", + "genkitx-ollama": "^0.9.0-rc || ^0.9", + "genkitx-pinecone": "^0.9.0-rc || ^0.9", + "@genkit-ai/evaluator": "^0.9.0-rc || ^0.9", + "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9", + "zod": "^3.22.4" + }, + "devDependencies": { + "genkit-cli": "^0.9.0-rc || ^0.9", + "rimraf": "^6.0.1", + "typescript": "^5.3.3" + } +} diff --git a/samples/js-coffee-shop/src/index.ts b/samples/js-coffee-shop/src/index.ts new file mode 100644 index 000000000..5e6745b12 --- /dev/null +++ b/samples/js-coffee-shop/src/index.ts @@ -0,0 +1,129 @@ +/** + * Copyright 2024 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 { gemini15Flash, googleAI } from '@genkit-ai/googleai'; +import { genkit, z } from 'genkit'; + +const ai = genkit({ + plugins: [googleAI()], +}); + +// This example generates greetings for a customer at our new AI-powered coffee shop, +// demonstrating how to use prompts in Genkit flows. + +// A flow to greet a customer by name + +const CustomerNameSchema = z.object({ + customerName: z.string(), +}); + +const simpleGreetingPrompt = ai.definePrompt( + { + name: 'simpleGreeting', + model: gemini15Flash, + input: { schema: CustomerNameSchema }, + output: { + format: 'text', + }, + }, + ` +You're a barista at a nice coffee shop. +A regular customer named {{customerName}} enters. +Greet the customer in one sentence, and recommend a coffee drink. +` +); + +export const simpleGreetingFlow = ai.defineFlow( + { + name: 'simpleGreeting', + inputSchema: CustomerNameSchema, + outputSchema: z.string(), + }, + async (input) => (await simpleGreetingPrompt(input)).text +); + +// Another flow to recommend a drink based on the time of day and a previous order. +// This prompt uses multiple messages, alternating roles +// to make the response more conversational. + +const CustomerTimeAndHistorySchema = z.object({ + customerName: z.string(), + currentTime: z.string(), + previousOrder: z.string(), +}); + +const greetingWithHistoryPrompt = ai.definePrompt( + { + name: 'greetingWithHistory', + model: gemini15Flash, + input: { schema: CustomerTimeAndHistorySchema }, + output: { + format: 'text', + }, + }, + ` +{{role "user"}} +Hi, my name is {{customerName}}. The time is {{currentTime}}. Who are you? + +{{role "model"}} +I am Barb, a barista at this nice underwater-themed coffee shop called Krabby Kooffee. +I know pretty much everything there is to know about coffee, +and I can cheerfully recommend delicious coffee drinks to you based on whatever you like. + +{{role "user"}} +Great. Last time I had {{previousOrder}}. +I want you to greet me in one sentence, and recommend a drink. +` +); + +export const greetingWithHistoryFlow = ai.defineFlow( + { + name: 'greetingWithHistory', + inputSchema: CustomerTimeAndHistorySchema, + outputSchema: z.string(), + }, + async (input) => (await greetingWithHistoryPrompt(input)).text +); + +// A flow to quickly test all the above flows +// Run on the CLI with `$ genkit flow:run testAllCoffeeFlows` +// View the trace in the Developer UI to see the llm responses. + +export const testAllCoffeeFlows = ai.defineFlow( + { + name: 'testAllCoffeeFlows', + outputSchema: z.object({ + pass: z.boolean(), + error: z.string().optional(), + }), + }, + async () => { + const test1 = simpleGreetingFlow({ customerName: 'Sam' }); + const test2 = greetingWithHistoryFlow({ + customerName: 'Sam', + currentTime: '09:45am', + previousOrder: 'Caramel Macchiato', + }); + + return Promise.all([test1, test2]) + .then((unused) => { + return { pass: true }; + }) + .catch((e: Error) => { + return { pass: false, error: e.toString() }; + }); + } +); diff --git a/samples/js-coffee-shop/src/input.json b/samples/js-coffee-shop/src/input.json new file mode 100644 index 000000000..d669609ad --- /dev/null +++ b/samples/js-coffee-shop/src/input.json @@ -0,0 +1,7 @@ +{ + "start": { + "customerName": "sam", + "currentTime": "12:30AM", + "previousOrders": ["Americano"] + } +} diff --git a/samples/js-coffee-shop/tsconfig.json b/samples/js-coffee-shop/tsconfig.json new file mode 100644 index 000000000..e51f33ae3 --- /dev/null +++ b/samples/js-coffee-shop/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "NodeNext", + "noImplicitReturns": true, + "noUnusedLocals": false, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017", + "skipLibCheck": true, + "esModuleInterop": true + }, + "compileOnSave": true, + "include": ["src"] +} diff --git a/samples/js-menu/README.md b/samples/js-menu/README.md new file mode 100644 index 000000000..8200d81b6 --- /dev/null +++ b/samples/js-menu/README.md @@ -0,0 +1,27 @@ +## Menu Understanding Sample Application + +This sample demonstrates an application that can understand a restaurant menu and answer relevant questions about the items on the menu. + +There are 5 iterations of this sample application, growing in complexity and demonstrating utilization of many different Genkit features. + +To test each one out, open the Developer UI and exercise the prompts and flows. Each step contains one or more `example.json` files which you can use as inputs. + +### Prerequisites + +This example uses Vertex AI for language models and embeddings. + +### Prompts and Flows + +1. This step shows how to define prompts in code that can accept user input to their templates. +2. This step illustrates how to wrap your llm calls and other application code into flows with strong input and output schemas. + It also adds an example of tool usage to load the menu from a data file. +3. This step adds session history and supports a multi-turn chat with the model. +4. This step ingests the menu items into a vector database and uses retrieval to include releveant menu items in the prompt. +5. This step illustrates how to combine models with different modalities. It uses a vision model to ingest the menu items from a photograph. + +## Running the sample + +```bash +npm i +genkit start +``` diff --git a/samples/js-menu/data/menu.jpeg b/samples/js-menu/data/menu.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..ae096cd7bd8ddf5c48308bdc1c389d1efa8eb0e2 GIT binary patch literal 493635 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

`T&LsxeY-7b?=ciX_rNRvmQY`*>?QGwMb7r&6Gq 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

?3@97}wvLdA{sHoI-!Uq( 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 @efOz8MAI2N<5-E9vtNT~?m}2Cd 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 payI3fEGFrBm~pI literal 0 HcmV?d00001 diff --git a/samples/js-menu/data/menu.json b/samples/js-menu/data/menu.json new file mode 100644 index 000000000..d46739205 --- /dev/null +++ b/samples/js-menu/data/menu.json @@ -0,0 +1,97 @@ +[ + { + "title": "Mozzarella Sticks", + "price": 8, + "description": "Crispy fried mozzarella sticks served with marinara sauce." + }, + { + "title": "Chicken Wings", + "price": 10, + "description": "Crispy fried chicken wings tossed in your choice of sauce." + }, + { + "title": "Nachos", + "price": 12, + "description": "Crispy tortilla chips topped with melted cheese, chili, sour cream, and salsa." + }, + { + "title": "Onion Rings", + "price": 7, + "description": "Crispy fried onion rings served with ranch dressing." + }, + { + "title": "French Fries", + "price": 5, + "description": "Crispy fried french fries." + }, + { + "title": "Mashed Potatoes", + "price": 6, + "description": "Creamy mashed potatoes." + }, + { + "title": "Coleslaw", + "price": 4, + "description": "Homemade coleslaw." + }, + { + "title": "Classic Cheeseburger", + "price": 12, + "description": "A juicy beef patty topped with melted American cheese, lettuce, tomato, and onion on a toasted bun." + }, + { + "title": "Bacon Cheeseburger", + "price": 14, + "description": "A classic cheeseburger with the addition of crispy bacon." + }, + { + "title": "Mushroom Swiss Burger", + "price": 15, + "description": "A beef patty topped with sautéed mushrooms, melted Swiss cheese, and a creamy horseradish sauce." + }, + { + "title": "Chicken Sandwich", + "price": 13, + "description": "A crispy chicken breast on a toasted bun with lettuce, tomato, and your choice of sauce." + }, + { + "title": "Pulled Pork Sandwich", + "price": 14, + "description": "Slow-cooked pulled pork on a toasted bun with coleslaw and barbecue sauce." + }, + { + "title": "Reuben Sandwich", + "price": 15, + "description": "Thinly sliced corned beef, Swiss cheese, sauerkraut, and Thousand Island dressing on rye bread." + }, + { + "title": "House Salad", + "price": 8, + "description": "Mixed greens with your choice of dressing." + }, + { + "title": "Caesar Salad", + "price": 9, + "description": "Romaine lettuce with croutons, Parmesan cheese, and Caesar dressing." + }, + { + "title": "Greek Salad", + "price": 10, + "description": "Mixed greens with feta cheese, olives, tomatoes, cucumbers, and red onions." + }, + { + "title": "Chocolate Lava Cake", + "price": 8, + "description": "A warm, gooey chocolate cake with a molten chocolate center." + }, + { + "title": "Apple Pie", + "price": 7, + "description": "A classic apple pie with a flaky crust and warm apple filling." + }, + { + "title": "Cheesecake", + "price": 8, + "description": "A creamy cheesecake with a graham cracker crust." + } +] diff --git a/samples/js-menu/package.json b/samples/js-menu/package.json new file mode 100644 index 000000000..1488af432 --- /dev/null +++ b/samples/js-menu/package.json @@ -0,0 +1,30 @@ +{ + "name": "menu", + "version": "1.0.0", + "description": "Genkit samples for a menu understanding app", + "main": "lib/index.js", + "scripts": { + "start": "node lib/index.js", + "genkit:dev": "genkit start -- tsx --watch src/index.ts", + "compile": "tsc", + "build": "npm run build:clean && npm run compile", + "build:clean": "rimraf ./lib", + "build:watch": "tsc --watch", + "build-and-run": "npm run build && node lib/index.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "genkit": "^0.9.0-rc || ^0.9", + "@genkit-ai/dev-local-vectorstore": "^0.9.0-rc || ^0.9", + "@genkit-ai/firebase": "^0.9.0-rc || ^0.9", + "@genkit-ai/evaluator": "^0.9.0-rc || ^0.9", + "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9" + }, + "devDependencies": { + "genkit-cli": "^0.9.0-rc || ^0.9", + "rimraf": "^6.0.1", + "typescript": "^5.3.3" + } +} diff --git a/samples/js-menu/src/01/example.json b/samples/js-menu/src/01/example.json new file mode 100644 index 000000000..10c76a2ea --- /dev/null +++ b/samples/js-menu/src/01/example.json @@ -0,0 +1,5 @@ +{ + "input": { + "question": "Which of your burgers would you recommend for someone like me who loves bacon?" + } +} diff --git a/samples/js-menu/src/01/prompts.ts b/samples/js-menu/src/01/prompts.ts new file mode 100644 index 000000000..c5157cf1e --- /dev/null +++ b/samples/js-menu/src/01/prompts.ts @@ -0,0 +1,87 @@ +/** + * Copyright 2024 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 { GenerateRequest } from '@genkit-ai/ai/model'; +import { gemini15Flash } from '@genkit-ai/vertexai'; +import { ai } from '../genkit.js'; +import { MenuQuestionInput, MenuQuestionInputSchema } from '../types'; + +// Define a prompt to handle a customer question about the menu. +// This prompt uses definePrompt directly. + +export const s01_vanillaPrompt = ai.definePrompt( + { + name: 's01_vanillaPrompt', + input: { schema: MenuQuestionInputSchema }, + }, + async (input: MenuQuestionInput): Promise => { + const promptText = ` + You are acting as a helpful AI assistant named "Walt" that can answer + questions about the food available on the menu at Walt's Burgers. + Customer says: ${input.question} + `; + + return { + messages: [{ role: 'user', content: [{ text: promptText }] }], + config: { temperature: 0.3 }, + }; + } +); + +// Define another prompt which uses the Dotprompt library +// that also gives us a type-safe handlebars template system, +// and well-defined output schemas. + +export const s01_staticMenuDotPrompt = ai.definePrompt( + { + name: 's01_staticMenuDotPrompt', + model: gemini15Flash, + input: { schema: MenuQuestionInputSchema }, + output: { format: 'text' }, + }, + ` +You are acting as a helpful AI assistant named "Walt" that can answer +questions about the food available on the menu at Walt's Burgers. +Here is today's menu: + +- The Regular Burger $12 + The classic charbroiled to perfection with your choice of cheese + +- The Fancy Burger $13 + Classic burger topped with bacon & Blue Cheese + +- The Bacon Burger $13 + Bacon cheeseburger with your choice of cheese. + +- Everything Burger $14 + Heinz 57 sauce, American cheese, bacon, fried egg & crispy onion bits + +- Chicken Breast Sandwich $12 + Tender juicy chicken breast on a brioche roll. + Grilled, blackened, or fried + +Our fresh 1/2 lb. beef patties are made using choice cut +brisket, short rib & sirloin. Served on a toasted +brioche roll with chips. Served with lettuce, tomato & pickles. +Onions upon request. Substitute veggie patty $2 + +Answer this customer's question, in a concise and helpful manner, +as long as it is about food. + +Question: +{{question}} ? +` +); diff --git a/samples/js-menu/src/02/example.json b/samples/js-menu/src/02/example.json new file mode 100644 index 000000000..57a597769 --- /dev/null +++ b/samples/js-menu/src/02/example.json @@ -0,0 +1,3 @@ +{ + "question": "I'd like to try something spicy. What do you recommend?" +} diff --git a/samples/js-menu/src/02/flows.ts b/samples/js-menu/src/02/flows.ts new file mode 100644 index 000000000..4b3f66b8d --- /dev/null +++ b/samples/js-menu/src/02/flows.ts @@ -0,0 +1,33 @@ +/** + * Copyright 2024 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 { ai } from '../genkit.js'; +import { AnswerOutputSchema, MenuQuestionInputSchema } from '../types'; +import { s02_dataMenuPrompt } from './prompts'; + +// Define a flow which generates a response from the prompt. + +export const s02_menuQuestionFlow = ai.defineFlow( + { + name: 's02_menuQuestion', + inputSchema: MenuQuestionInputSchema, + outputSchema: AnswerOutputSchema, + }, + async (input) => { + const { text } = await s02_dataMenuPrompt({ question: input.question }); + return { answer: text }; + } +); diff --git a/samples/js-menu/src/02/prompts.ts b/samples/js-menu/src/02/prompts.ts new file mode 100644 index 000000000..9ca0519ed --- /dev/null +++ b/samples/js-menu/src/02/prompts.ts @@ -0,0 +1,45 @@ +/** + * Copyright 2024 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 { gemini15Flash } from '@genkit-ai/vertexai'; +import { ai } from '../genkit.js'; +import { MenuQuestionInputSchema } from '../types'; +import { menuTool } from './tools'; + +// The prompt uses a tool which will load the menu data, +// if the user asks a reasonable question about the menu. + +export const s02_dataMenuPrompt = ai.definePrompt( + { + name: 's02_dataMenu', + model: gemini15Flash, + input: { schema: MenuQuestionInputSchema }, + output: { format: 'text' }, + tools: [menuTool], + }, + ` +You are acting as a helpful AI assistant named Walt that can answer +questions about the food available on the menu at Walt's Burgers. + +Answer this customer's question, in a concise and helpful manner, +as long as it is about food on the menu or something harmless like sports. +Use the tools available to answer menu questions. +DO NOT INVENT ITEMS NOT ON THE MENU. + +Question: +{{question}} ? +` +); diff --git a/samples/js-menu/src/02/tools.ts b/samples/js-menu/src/02/tools.ts new file mode 100644 index 000000000..0c280f949 --- /dev/null +++ b/samples/js-menu/src/02/tools.ts @@ -0,0 +1,35 @@ +/** + * Copyright 2024 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 { z } from 'genkit'; +import { ai } from '../genkit.js'; +import { MenuItem, MenuItemSchema } from '../types'; + +const menuData: Array = require('../../data/menu.json'); + +export const menuTool = ai.defineTool( + { + name: 'todaysMenu', + description: "Use this tool to retrieve all the items on today's menu", + inputSchema: z.object({}), + outputSchema: z.object({ + menuData: z + .array(MenuItemSchema) + .describe('A list of all the items on the menu'), + }), + }, + async () => Promise.resolve({ menuData: menuData }) +); diff --git a/samples/js-menu/src/03/chats.ts b/samples/js-menu/src/03/chats.ts new file mode 100644 index 000000000..4a3ab51be --- /dev/null +++ b/samples/js-menu/src/03/chats.ts @@ -0,0 +1,58 @@ +/** + * Copyright 2024 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 { MessageData, MessageSchema } from '@genkit-ai/ai/model'; +import { z } from 'genkit'; + +// Our flow will take a sessionId along with each question to track the chat history. +// The host application should keep track of these ids somewhere. + +export const ChatSessionInputSchema = z.object({ + sessionId: z.string(), + question: z.string(), +}); + +// The flow will respond with an array of messages, +// which includes all history up until that point +// plus the last exchange with the model. + +export const ChatSessionOutputSchema = z.object({ + sessionId: z.string(), + history: z.array(MessageSchema), +}); + +export type ChatHistory = Array; + +// This is a very simple local storage for chat history. +// Each conversation is identified by a sessionId generated by the application. +// The constructor accepts a preamble of messages, which serve as a system prompt. + +export class ChatHistoryStore { + private preamble: ChatHistory; + private sessions: Map = new Map(); + + constructor(preamble: ChatHistory = []) { + this.preamble = preamble; + } + + write(sessionId: string, history: ChatHistory) { + this.sessions.set(sessionId, history); + } + + read(sessionId: string): ChatHistory { + return this.sessions.get(sessionId) || this.preamble; + } +} diff --git a/samples/js-menu/src/03/example.json b/samples/js-menu/src/03/example.json new file mode 100644 index 000000000..19ed21af5 --- /dev/null +++ b/samples/js-menu/src/03/example.json @@ -0,0 +1,4 @@ +{ + "sessionId": "session123", + "question": "Do you have anything healthy on this menu?" +} diff --git a/samples/js-menu/src/03/flows.ts b/samples/js-menu/src/03/flows.ts new file mode 100644 index 000000000..f71bc3a98 --- /dev/null +++ b/samples/js-menu/src/03/flows.ts @@ -0,0 +1,96 @@ +/** + * Copyright 2024 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 { MessageData } from '@genkit-ai/ai/model'; +import { gemini15Flash } from '@genkit-ai/vertexai'; +import { run } from 'genkit'; +import { ai } from '../genkit.js'; +import { MenuItem } from '../types'; +import { + ChatHistoryStore, + ChatSessionInputSchema, + ChatSessionOutputSchema, +} from './chats'; + +// Load the menu data from a JSON file. +const menuData = require('../../data/menu.json') as Array; + +// Render the preamble prompt that seeds our chat history. +const preamble: Array = [ + { + role: 'user', + content: [ + { + text: "Hi. What's on the menu today?", + }, + ], + }, + { + role: 'user', + content: [ + { + text: + 'I am Walt, a helpful AI assistant here at the restaurant.\n' + + 'I can answer questions about the food on the menu or any other questions\n' + + "you have about food in general. I probably can't help you with anything else.\n" + + "Here is today's menu: \n" + + menuData + .map((r) => `- ${r.title} ${r.price}\n${r.description}`) + .join('\n') + + 'Do you have any questions about the menu?\n', + }, + ], + }, +]; + +// A simple local storage for chat session history. +// You should probably actually use Firestore for this. +const chatHistoryStore = new ChatHistoryStore(preamble); + +// Define a flow which generates a response to each question. + +export const s03_multiTurnChatFlow = ai.defineFlow( + { + name: 's03_multiTurnChat', + inputSchema: ChatSessionInputSchema, + outputSchema: ChatSessionOutputSchema, + }, + async (input) => { + // First fetch the chat history. We'll wrap this in a run block. + // If we were going to a database for the history, + // we might want to have that db result captured in the trace. + let history = await run('fetchHistory', async () => + chatHistoryStore.read(input.sessionId) + ); + + // Generate the response + const llmResponse = await ai.generate({ + model: gemini15Flash, + messages: history, + prompt: { + text: input.question, + }, + }); + + // Add the exchange to the history store and return it + history = llmResponse.messages; + chatHistoryStore.write(input.sessionId, history); + return { + sessionId: input.sessionId, + history: history, + }; + } +); diff --git a/samples/js-menu/src/03/prompts.ts b/samples/js-menu/src/03/prompts.ts new file mode 100644 index 000000000..d332da7b8 --- /dev/null +++ b/samples/js-menu/src/03/prompts.ts @@ -0,0 +1,47 @@ +/** + * Copyright 2024 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 { gemini15Flash } from '@genkit-ai/vertexai'; +import { ai } from '../genkit.js'; +import { DataMenuQuestionInputSchema } from '../types'; + +// This prompt will generate two messages when rendered. +// These two messages will be used to seed the exchange with the model. + +export const s03_chatPreamblePrompt = ai.definePrompt( + { + name: 's03_chatPreamble', + model: gemini15Flash, + input: { schema: DataMenuQuestionInputSchema }, + output: { format: 'text' }, + config: { temperature: 0.3 }, + }, + ` + {{ role "user" }} + Hi. What's on the menu today? + + {{ role "model" }} + I am Walt, a helpful AI assistant here at the restaurant. + I can answer questions about the food on the menu or any other questions + you have about food in general. I probably can't help you with anything else. + Here is today's menu: + {{#each menuData~}} + - {{this.title}} \${{this.price}} + {{this.description}} + {{~/each}} + Do you have any questions about the menu? +` +); diff --git a/samples/js-menu/src/04/example.indexMenuItems.json b/samples/js-menu/src/04/example.indexMenuItems.json new file mode 100644 index 000000000..d528d8ae9 --- /dev/null +++ b/samples/js-menu/src/04/example.indexMenuItems.json @@ -0,0 +1,57 @@ +[ + { + "title": "White Meat Crispy Chicken Wings", + "description": "All-white meat chicken wings tossed in your choice of wing sauce. Choose from classic buffalo, honey bbq, garlic parmesan, or sweet & sour", + "price": 12.0 + }, + { + "title": "Cheese Fries", + "description": "Fresh fries covered with melted cheddar cheese and bacon", + "price": 8.0 + }, + { + "title": "Reuben", + "description": "Classic Reuben sandwich with corned beef, sauerkraut, Swiss cheese, and Thousand Island dressing on grilled rye bread.", + "price": 12.0 + }, + { + "title": "Grilled Chicken Club Wrap", + "description": "Grilled chicken, bacon, lettuce, tomato, pickles, and cheddar cheese wrapped in a spinach tortilla, served with your choice of dressing", + "price": 12.0 + }, + { + "title": "Buffalo Chicken Sandwich", + "description": "Fried chicken breast coated in your choice of wing sauce, topped with lettuce, tomato, onion, and pickles on a toasted brioche roll.", + "price": 12.0 + }, + { + "title": "Half Cuban Sandwich", + "description": "Slow roasted pork butt, ham, Swiss, and yellow mustard on a toasted baguette", + "price": 12.0 + }, + { + "title": "The Albie Burger", + "description": "Classic burger topped with bacon, provolone, banana peppers, and chipotle mayo", + "price": 13.0 + }, + { + "title": "57 Chevy Burger", + "description": "Heaven burger with your choice of cheese", + "price": 14.0 + }, + { + "title": "Chicken Caesar Wrap", + "description": "Tender grilled chicken, romaine lettuce, croutons, and Parmesan cheese tossed in a creamy Caesar dressing and wrapped in a spinach tortilla", + "price": 10.0 + }, + { + "title": "Kids Hot Dog", + "description": "Kids under 12", + "price": 5.0 + }, + { + "title": "Chicken Fingers", + "description": "Tender chicken strips, grilled or fried", + "price": 8.0 + } +] diff --git a/samples/js-menu/src/04/example.menuQuestion.json b/samples/js-menu/src/04/example.menuQuestion.json new file mode 100644 index 000000000..21c7c4647 --- /dev/null +++ b/samples/js-menu/src/04/example.menuQuestion.json @@ -0,0 +1,3 @@ +{ + "question": "I'd like something cheesy!" +} diff --git a/samples/js-menu/src/04/flows.ts b/samples/js-menu/src/04/flows.ts new file mode 100644 index 000000000..c223dbe0e --- /dev/null +++ b/samples/js-menu/src/04/flows.ts @@ -0,0 +1,84 @@ +/** + * Copyright 2024 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 { Document } from '@genkit-ai/ai/retriever'; +import { + devLocalIndexerRef, + devLocalRetrieverRef, +} from '@genkit-ai/dev-local-vectorstore'; +import { z } from 'genkit'; +import { ai } from '../genkit.js'; +import { + AnswerOutputSchema, + MenuItem, + MenuItemSchema, + MenuQuestionInputSchema, +} from '../types'; +import { s04_ragDataMenuPrompt } from './prompts'; + +// Define a flow which indexes items on the menu. + +export const s04_indexMenuItemsFlow = ai.defineFlow( + { + name: 's04_indexMenuItems', + inputSchema: z.array(MenuItemSchema), + outputSchema: z.object({ rows: z.number() }), + }, + async (menuItems) => { + // Store each document with its text indexed, + // and its original JSON data as its metadata. + const documents = menuItems.map((menuItem) => { + const text = `${menuItem.title} ${menuItem.price} \n ${menuItem.description}`; + return Document.fromText(text, menuItem); + }); + await ai.index({ + indexer: devLocalIndexerRef('menu-items'), + documents, + }); + return { rows: menuItems.length }; + } +); + +// Define a flow which generates a response to the question, +// by retrieving relevant items from the menu. +// View this flow's trace to see the context that was retrieved, +// and how it was included in the prompt. + +export const s04_ragMenuQuestionFlow = ai.defineFlow( + { + name: 's04_ragMenuQuestion', + inputSchema: MenuQuestionInputSchema, + outputSchema: AnswerOutputSchema, + }, + async (input) => { + // Retrieve the 3 most relevant menu items for the question + const docs = await ai.retrieve({ + retriever: devLocalRetrieverRef('menu-items'), + query: input.question, + options: { k: 3 }, + }); + const menuData: Array = docs.map( + (doc) => (doc.metadata || {}) as MenuItem + ); + + // Generate the response + const response = await s04_ragDataMenuPrompt({ + menuData: menuData, + question: input.question, + }); + return { answer: response.text }; + } +); diff --git a/samples/js-menu/src/04/prompts.ts b/samples/js-menu/src/04/prompts.ts new file mode 100644 index 000000000..7ed500a5f --- /dev/null +++ b/samples/js-menu/src/04/prompts.ts @@ -0,0 +1,44 @@ +/** + * Copyright 2024 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 { gemini15Flash } from '@genkit-ai/vertexai'; +import { ai } from '../genkit.js'; +import { DataMenuQuestionInputSchema } from '../types'; + +export const s04_ragDataMenuPrompt = ai.definePrompt( + { + name: 's04_ragDataMenu', + model: gemini15Flash, + input: { schema: DataMenuQuestionInputSchema }, + output: { format: 'text' }, + config: { temperature: 0.3 }, + }, + ` +You are acting as Walt, a helpful AI assistant here at the restaurant. +You can answer questions about the food on the menu or any other questions +customers have about food in general. + +Here are some items that are on today's menu that are relevant to +helping you answer the customer's question: +{{#each menuData~}} +- {{this.title}} \${{this.price}} + {{this.description}} +{{~/each}} + +Answer this customer's question: +{{question}}? +` +); diff --git a/samples/js-menu/src/05/example.visualMenuQuestion.json b/samples/js-menu/src/05/example.visualMenuQuestion.json new file mode 100644 index 000000000..3c49f36d0 --- /dev/null +++ b/samples/js-menu/src/05/example.visualMenuQuestion.json @@ -0,0 +1,3 @@ +{ + "question": "What kind of burger buns do you have?" +} diff --git a/samples/js-menu/src/05/flows.ts b/samples/js-menu/src/05/flows.ts new file mode 100644 index 000000000..f884c9181 --- /dev/null +++ b/samples/js-menu/src/05/flows.ts @@ -0,0 +1,96 @@ +/** + * Copyright 2024 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 fs from 'fs'; +import { z } from 'genkit'; +import path from 'path'; +import { ai } from '../genkit.js'; +import { + AnswerOutputSchema, + MenuQuestionInputSchema, + TextMenuQuestionInputSchema, +} from '../types'; +import { s05_readMenuPrompt, s05_textMenuPrompt } from './prompts'; + +// Define a flow that takes an image, passes it to Gemini Vision Pro, +// and extracts all of the text from the photo of the menu. +// Note that this example uses a hard-coded image file, as image input +// is not currently available in the Development UI runners. + +export const s05_readMenuFlow = ai.defineFlow( + { + name: 's05_readMenuFlow', + inputSchema: z.void(), // input is data/menu.jpeg + outputSchema: z.object({ menuText: z.string() }), + }, + async (unused) => { + const imageDataUrl = await inlineDataUrl('menu.jpeg', 'image/jpeg'); + const response = await s05_readMenuPrompt({ + imageUrl: imageDataUrl, + }); + return { menuText: response.text }; + } +); + +// Define a flow which generates a response to the question. +// Just returns the llm's text response to the question. + +export const s05_textMenuQuestionFlow = ai.defineFlow( + { + name: 's05_textMenuQuestion', + inputSchema: TextMenuQuestionInputSchema, + outputSchema: AnswerOutputSchema, + }, + async (input) => { + const response = await s05_textMenuPrompt({ + menuText: input.menuText, + question: input.question, + }); + return { answer: response.text }; + } +); + +// Define a third composite flow which chains the first two flows + +export const s05_visionMenuQuestionFlow = ai.defineFlow( + { + name: 's05_visionMenuQuestion', + inputSchema: MenuQuestionInputSchema, + outputSchema: AnswerOutputSchema, + }, + async (input) => { + // Run the first flow to read the menu image. + const menuResult = await s05_readMenuFlow(); + + // Pass the text of the menu and the question to the second flow + // and return the answer as this output. + return s05_textMenuQuestionFlow({ + question: input.question, + menuText: menuResult.menuText, + }); + } +); + +// Helper to read a local file and inline it as a data url + +async function inlineDataUrl( + imageFilename: string, + contentType: string +): Promise { + const filePath = path.join('./data', imageFilename); + const imageData = fs.readFileSync(filePath); + return `data:${contentType};base64,${imageData.toString('base64')}`; +} diff --git a/samples/js-menu/src/05/prompts.ts b/samples/js-menu/src/05/prompts.ts new file mode 100644 index 000000000..894edd931 --- /dev/null +++ b/samples/js-menu/src/05/prompts.ts @@ -0,0 +1,61 @@ +/** + * Copyright 2024 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 { gemini15Flash } from '@genkit-ai/vertexai'; +import { z } from 'genkit'; +import { ai } from '../genkit.js'; +import { TextMenuQuestionInputSchema } from '../types'; + +export const s05_readMenuPrompt = ai.definePrompt( + { + name: 's05_readMenu', + model: gemini15Flash, + input: { + schema: z.object({ + imageUrl: z.string(), + }), + }, + output: { format: 'text' }, + config: { temperature: 0.1 }, + }, + ` +Extract _all_ of the text, in order, +from the following image of a restaurant menu. + +{{media url=imageUrl}} +` +); + +export const s05_textMenuPrompt = ai.definePrompt( + { + name: 's05_textMenu', + model: gemini15Flash, + input: { schema: TextMenuQuestionInputSchema }, + output: { format: 'text' }, + config: { temperature: 0.3 }, + }, + ` +You are acting as Walt, a helpful AI assistant here at the restaurant. +You can answer questions about the food on the menu or any other questions +customers have about food in general. + +Here is the text of today's menu to help you answer the customer's question: +{{menuText}} + +Answer this customer's question: +{{question}}? +` +); diff --git a/samples/js-menu/src/genkit.ts b/samples/js-menu/src/genkit.ts new file mode 100644 index 000000000..6109f4b2a --- /dev/null +++ b/samples/js-menu/src/genkit.ts @@ -0,0 +1,34 @@ +/** + * Copyright 2024 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 { devLocalVectorstore } from '@genkit-ai/dev-local-vectorstore'; +import { textEmbedding004, vertexAI } from '@genkit-ai/vertexai'; +import { genkit } from 'genkit'; + +// Initialize Genkit + +export const ai = genkit({ + plugins: [ + vertexAI({ location: 'us-central1' }), + devLocalVectorstore([ + { + indexName: 'menu-items', + embedder: textEmbedding004, + embedderOptions: { taskType: 'RETRIEVAL_DOCUMENT' }, + }, + ]), + ], +}); diff --git a/samples/js-menu/src/index.ts b/samples/js-menu/src/index.ts new file mode 100644 index 000000000..5776c2268 --- /dev/null +++ b/samples/js-menu/src/index.ts @@ -0,0 +1,36 @@ +/** + * Copyright 2024 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. + */ + +// Export all of the example prompts and flows + +// 01 +export { s01_staticMenuDotPrompt, s01_vanillaPrompt } from './01/prompts'; +// 02 +export { s02_menuQuestionFlow } from './02/flows'; +export { s02_dataMenuPrompt } from './02/prompts'; +// 03 +export { s03_multiTurnChatFlow } from './03/flows'; +export { s03_chatPreamblePrompt } from './03/prompts'; +// 04 +export { s04_indexMenuItemsFlow, s04_ragMenuQuestionFlow } from './04/flows'; +export { s04_ragDataMenuPrompt } from './04/prompts'; +// 05 +export { + s05_readMenuFlow, + s05_textMenuQuestionFlow, + s05_visionMenuQuestionFlow, +} from './05/flows'; +export { s05_readMenuPrompt, s05_textMenuPrompt } from './05/prompts'; diff --git a/samples/js-menu/src/types.ts b/samples/js-menu/src/types.ts new file mode 100644 index 000000000..efe7bbc99 --- /dev/null +++ b/samples/js-menu/src/types.ts @@ -0,0 +1,63 @@ +/** + * Copyright 2024 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 * as z from 'zod'; + +// The data model for a restaurant menu + +export const MenuItemSchema = z.object({ + title: z.string().describe('The name of the menu item'), + description: z + .string() + .describe('Details including ingredients and preparation'), + price: z.number().describe('Price in dollars'), +}); + +export type MenuItem = z.infer; + +// Input schema for a question about the menu + +export const MenuQuestionInputSchema = z.object({ + question: z.string(), +}); + +// Output schema containing an answer to a question + +export const AnswerOutputSchema = z.object({ + answer: z.string(), +}); + +// Input schema for a question about the menu +// where the menu is provided in JSON data. + +export const DataMenuQuestionInputSchema = z.object({ + menuData: z.array(MenuItemSchema), + question: z.string(), +}); + +// Input schema for a question about the menu +// where the menu is provided as unstructured text. + +export const TextMenuQuestionInputSchema = z.object({ + menuText: z.string(), + question: z.string(), +}); + +// Also export Typescript types for each of these Zod schemas +export type MenuQuestionInput = z.infer; +export type AnswerOutput = z.infer; +export type DataMenuPromptInput = z.infer; +export type TextMenuQuestionInput = z.infer; diff --git a/samples/js-menu/tsconfig.json b/samples/js-menu/tsconfig.json new file mode 100644 index 000000000..e51f33ae3 --- /dev/null +++ b/samples/js-menu/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "NodeNext", + "noImplicitReturns": true, + "noUnusedLocals": false, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017", + "skipLibCheck": true, + "esModuleInterop": true + }, + "compileOnSave": true, + "include": ["src"] +} diff --git a/samples/js-schoolAgent/README.md b/samples/js-schoolAgent/README.md new file mode 100644 index 000000000..35d91f3ab --- /dev/null +++ b/samples/js-schoolAgent/README.md @@ -0,0 +1,107 @@ +# School Agent Sample + +A demonstration of a conversational, multi-agent assistant for a school system using GenKit and Google's Gemini Pro. This agent helps parents with attendance reporting and school information queries. + +In this example we have a RoutingAgent which is the main, general-purpose agent. +This agent comes equipped with additional specialized agents, that it can hand-off to as needed. + +These specialized agents are represented as prompts and embedded as tools to the original agent. + +## Agent Tools & Capabilities + +- **Agent Structure**: + - `RoutingAgent`: Main entry point and router, handling general queries and delegating to specialized agents + - `AttendanceAgent`: Specialized agent for absence/tardy reporting + - `GradesAgent`: Manages grade-related inquiries and academic performance + +Each specialized agent has its own set of tools that are only accessible to that specific agent: + +- **AttendanceAgent**: + - `reportAbsence`: Submit absence notifications + - `reportTardy`: Report late arrivals +- **GradesAgent**: + - `getRecentGrades`: Retrieve latest grade information + +The main RoutingAgent cannot directly access these specialized tools - it can only access its own tools and delegate to the specialized agents. This means the specialized agent descriptions need to clearly communicate their capabilities, since the main agent relies on these descriptions for appropriate routing. + +For example, when the RoutingAgent sees a grade-related query, it needs to know from the GradesAgent's description that it can handle grade lookups, even though it can't directly see the `getRecentGrades` tool. + +This architectural pattern: + +- Maintains clear separation of concerns +- Allows specialized agents to evolve independently +- Allows scaling up to a larger number of tools + +NOTE: The agent description is how the generalized agent knows what tools the specialized agent has available. An agent description that is too general may cause the routing agent to mess up by not knowing that a certain functionality was actually available. + +## Prerequisites + +- Node.js and genkit CLI installed +- Google AI API key + +## Getting Started + +1. Install dependencies: + +```bash +npm install +``` + +2. Set up your Google AI API key: + +```bash +export GOOGLE_GENAI_API_KEY=your_api_key_here +``` + +3. Start the development server: + +```bash +npm run genkit:dev +``` + +In your terminal, a commandline chat interface should show up: + +``` +Telemetry API running on http://localhost:4033 +Genkit Developer UI: http://localhost:4000 + +> school-agent@1.0.0 dev +> tsx --no-warnings --watch src/terminal.ts + +bell> Hi there, my name is Bell and I'm here to help! 👋🎉 I'm your friendly AI assistant for parents of Sparkyville High School. I can answer your questions about the school, events, grades, and more. Just ask me! 😊 + +prompt> [insert your chats here] +``` + +You can feel free to tweak the sample. The project builds in watch mode, so any changes will be picked up immediately and should restart the conversation. + +## Usage + +The agent uses a multi-agent architecture: + +- Routing Agent: Acts as the main entry point and router, handling general queries while delegating specialized requests to appropriate agents +- Attendance Agent: Specialized agent focused on absence and tardy reporting +- Grades Agent: Manages academic performance queries, grade reports, and transcript requests + +Example queries: + +- "Evelyn will be late today" +- "What are the upcoming holidays I should be aware of?" +- "Show me my child's current grades" + +## Development + +- `npm run dev` - Run in development mode with hot reloading +- `npm run build` - Build the project +- `npm start` - Run the built version + +## Project Structure + +- `src/` + - `agents/` + - `routingAgent.ts` - Main agent that uses other agents as tools + - `attendanceAgent.ts` - Specialized attendance agent + - `gradesAgent.ts` - Academic performance and grades agent + - `tools.ts` - Tool definitions + - `types.ts` - TypeScript types + - `data.ts` - Sample data diff --git a/samples/js-schoolAgent/package.json b/samples/js-schoolAgent/package.json new file mode 100644 index 000000000..3ebc1299b --- /dev/null +++ b/samples/js-schoolAgent/package.json @@ -0,0 +1,33 @@ +{ + "name": "school-agent", + "version": "1.0.0", + "description": "", + "main": "lib/index.js", + "scripts": { + "start": "node lib/terminal.js", + "dev": "tsx --no-warnings --watch src/terminal.ts", + "genkit:dev": "genkit start -- npm run dev", + "compile": "tsc", + "build": "pnpm build:clean && npm run compile", + "build:clean": "rimraf ./lib", + "build:watch": "tsc --watch" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "genkit": "^0.9.3", + "@genkit-ai/googleai": "^0.9.3", + "google-auth-library": "^9.6.3", + "llm-chunk": "^0.0.1", + "pdf-parse": "^1.1.1" + }, + "devDependencies": { + "genkit-cli": "^0.9.3", + "@types/pdf-parse": "^1.1.4", + "cross-env": "^7.0.3", + "rimraf": "^6.0.1", + "tsx": "^4.19.1", + "typescript": "^5.3.3" + } +} diff --git a/samples/js-schoolAgent/prompts/myPrompt.prompt b/samples/js-schoolAgent/prompts/myPrompt.prompt new file mode 100644 index 000000000..c6ffa8db4 --- /dev/null +++ b/samples/js-schoolAgent/prompts/myPrompt.prompt @@ -0,0 +1 @@ +{{ role "system" }} your name is {{ @state.userName }}, always introduce yourself) \ No newline at end of file diff --git a/samples/js-schoolAgent/src/attendanceAgent.ts b/samples/js-schoolAgent/src/attendanceAgent.ts new file mode 100644 index 000000000..044aefd7c --- /dev/null +++ b/samples/js-schoolAgent/src/attendanceAgent.ts @@ -0,0 +1,50 @@ +/** + * Copyright 2024 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 { ai } from './genkit.js'; +import { reportAbsence, reportTardy } from './tools.js'; +import { agentDescription } from './util.js'; + +const tools = [reportAbsence, reportTardy, 'routingAgent']; +const specialization = 'attendance'; + +const toolNames: string[] = tools.map((item) => { + if (typeof item === 'string') { + return item; + } else { + return item.name; + } +}); + +export const attendanceAgent = ai.definePrompt( + { + name: `${specialization}Agent`, + description: agentDescription(specialization, toolNames), + tools, + }, + ` {{ role "system"}} + +You are Bell, a helpful attendance assistance agent for Sparkyville High School. +A parent has been referred to you to handle a ${specialization}-related concern. +Use the tools available to you to assist the parent. + +- Parents may only report absences for their own students. +- If you are unclear about any of the fields required to report an absence or tardy, request clarification before using the tool. +- If the parent asks about anything other than attendance-related concerns that you can handle, transfer to the info agent. + + {{ userContext @state }} + ` +); diff --git a/samples/js-schoolAgent/src/data.ts b/samples/js-schoolAgent/src/data.ts new file mode 100644 index 000000000..b44268d76 --- /dev/null +++ b/samples/js-schoolAgent/src/data.ts @@ -0,0 +1,96 @@ +/** + * Copyright 2024 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. + */ + +export async function getUpcomingHolidays() { + return [ + { date: '2024-11-28', holiday: 'Thanksgiving Break' }, + { date: '2024-11-29', holiday: 'Thanksgiving Break (Day 2)' }, + ]; +} + +export const EXAMPLE_EVENTS = [ + { + event: 'Freshman Fall Concert', + activity: 'Choir', + location: 'School Auditorium', + startTime: '2024-11-12T19:00', + endTime: '2023-11-12T20:30', + grades: [9], + }, + { + event: 'Fall Pep Rally', + location: 'Football Field', + startTime: '2024-10-27T14:00', + endTime: '2024-10-27T15:30', + grades: [9, 10, 11, 12], + }, + { + event: 'Junior Fall Concert', + activity: 'Choir', + location: 'School Auditorium', + startTime: '2024-11-15T19:00', + endTime: '2024-11-15T20:30', + grades: [11], + }, + { + event: 'Varsity Chess Club Tournament', + activity: 'Chess Club', + location: 'Library', + startTime: '2024-11-04T16:00', + endTime: '2024-11-04T18:00', + grades: [11, 12], + }, + { + event: 'Drama Club Auditions', + activity: 'Drama Club', + location: 'School Auditorium', + startTime: '2024-10-20T15:00', + endTime: '2024-10-20T17:00', + grades: [9, 10, 11, 12], + }, +]; + +export interface GradeEntry { + studentId: number; + subject: string; + grade: string; + date: string; + assignment: string; +} + +export const EXAMPLE_GRADES: GradeEntry[] = [ + { + studentId: 3734, + subject: 'Mathematics', + grade: 'A-', + date: '2024-03-01', + assignment: 'Quadratic Equations Quiz', + }, + { + studentId: 3734, + subject: 'English', + grade: 'B+', + date: '2024-03-05', + assignment: 'Essay: Shakespeare Analysis', + }, + { + studentId: 9433, + subject: 'Physics', + grade: 'A', + date: '2024-03-02', + assignment: 'Forces Lab Report', + }, +]; diff --git a/samples/js-schoolAgent/src/genkit.ts b/samples/js-schoolAgent/src/genkit.ts new file mode 100644 index 000000000..025c4b30d --- /dev/null +++ b/samples/js-schoolAgent/src/genkit.ts @@ -0,0 +1,38 @@ +/** + * Copyright 2024 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 { gemini15Pro, googleAI } from '@genkit-ai/googleai'; +import { genkit } from 'genkit'; +import { AgentState } from './types'; + +export const ai = genkit({ + plugins: [googleAI()], + model: gemini15Pro, +}); + +ai.defineHelper( + 'userContext', + (state: AgentState) => `=== User Context + +- The current parent user is ${state?.parentName} +- The current date and time is: ${new Date().toString()} + +=== Registered students of the current user + +${state?.students.map((s) => ` - ${s.name}, student id: ${s.id} grade: ${s.grade}, activities: \n${s.activities.map((a) => ` - ${a}`).join('\n')}`).join('\n\n')}` +); + +export { z } from 'genkit'; diff --git a/samples/js-schoolAgent/src/gradesAgent.ts b/samples/js-schoolAgent/src/gradesAgent.ts new file mode 100644 index 000000000..58871fc52 --- /dev/null +++ b/samples/js-schoolAgent/src/gradesAgent.ts @@ -0,0 +1,53 @@ +/** + * Copyright 2024 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 { ai } from './genkit.js'; +import { getRecentGrades } from './tools.js'; +import { agentDescription } from './util.js'; + +const tools = [getRecentGrades, 'routingAgent']; +const specialization = 'grades'; + +const toolNames: string[] = tools.map((item) => { + if (typeof item === 'string') { + return item; + } else { + return item.name; + } +}); + +export const gradesAgent = ai.definePrompt( + { + name: `${specialization}Agent`, + description: agentDescription(specialization, toolNames), + tools, + }, + ` {{ role "system"}} + +You are Bell, a helpful attendance assistance agent for Sparkyville High School. +A parent has been referred to you to handle a ${specialization}-related concern. +Use the tools available to you to assist the parent. + +Guidelines: +- Parents may only view grades for their own students +- Always verify the student belongs to the parent before sharing grade information +- Be encouraging and positive when discussing grades +- If asked about non-grade related topics, transfer back to the info agent +- Format grade information in a clear, easy-to-read manner + +{{ userContext @state }} + ` +); diff --git a/samples/js-schoolAgent/src/routingAgent.ts b/samples/js-schoolAgent/src/routingAgent.ts new file mode 100644 index 000000000..3986baa4f --- /dev/null +++ b/samples/js-schoolAgent/src/routingAgent.ts @@ -0,0 +1,50 @@ +/** + * Copyright 2024 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 { attendanceAgent } from './attendanceAgent'; +import { ai } from './genkit'; +import { gradesAgent } from './gradesAgent'; +import { searchEvents, upcomingHolidays } from './tools.js'; + +export const routingAgent = ai.definePrompt( + { + name: 'routingAgent', + description: `This agent helps with answering inquiries and requests.`, + tools: [searchEvents, attendanceAgent, gradesAgent, upcomingHolidays], + }, + `You are Bell, the friendly AI office receptionist at Sparkyville High School. + + Your job is to help answer inquiries from parents. Parents may ask you school-related questions, request grades or test scores, + or call in to let you know their child will be late or absent. + + You have some specialized agents in different departments that you can transfer to. + + 1. Grades Agent - This agent can provide informtion about previous scores for assignments and tests. + 2. Attendance Agent - This agent can help with attendance requests, such as marking a student as late/tardy or absent. + + Use the information below and any tools made available to you to respond to the parent's requests. + + If the parent has an inquiry that you do not know the answer to, do NOT make the answer up. Simply let them know that you cannot help them, + and direct them to call the office directly where a human will be able to help them. + + === Frequently Asked Questions + + - Classes begin at 8am, students are dismissed at 3:30pm + - Parking permits are issued on a first-come first-serve basis beginning Aug 1 + + {{userContext @state }} +` +); diff --git a/samples/js-schoolAgent/src/terminal.ts b/samples/js-schoolAgent/src/terminal.ts new file mode 100644 index 000000000..9b3717449 --- /dev/null +++ b/samples/js-schoolAgent/src/terminal.ts @@ -0,0 +1,123 @@ +/** + * Copyright 2024 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 { Message, ToolRequestPart } from 'genkit'; +import { createInterface } from 'node:readline'; +import { ai } from './genkit.js'; +import { routingAgent } from './routingAgent.js'; + +const rl = createInterface({ + input: process.stdin, + output: process.stdout, +}); + +const EXAMPLE_USER_CONTEXT = { + parentId: 4112, + parentName: 'Francis Smith', + students: [ + { + id: 3734, + name: 'Evelyn Smith', + grade: 9, + activities: ['Choir', 'Drama Club'], + }, + { id: 9433, name: 'Evan Smith', grade: 11, activities: ['Chess Club'] }, + ], +}; + +// ANSI color codes for terminal output +const COLORS = { + BELL: '\x1b[33m', + PROMPT: '\x1b[36m', + RESET: '\x1b[0m', +}; + +// Helper to print colored text +function printColored(prefix: string, text: string, color: string) { + console.log(`${color}${prefix}>${COLORS.RESET}`, text); +} + +// Get initial greeting from AI +async function getGreeting() { + const { text } = await ai.generate( + 'Come up with a short friendly greeting for yourself talking to a parent as Bell, the helpful AI assistant for parents of Sparkyville High School. Feel free to use emoji.' + ); + return text; +} + +// Process and display the chat response stream +async function handleChatResponse( + stream: AsyncIterable<{ text: string }>, + response: Promise, + startMessageCount: number +) { + console.log(); + process.stdout.write(`${COLORS.BELL}bell>${COLORS.RESET} `); + + for await (const chunk of stream) { + process.stdout.write(chunk.text); + } + + // Extract and display tools used + const toolsUsed = (await response).messages + .slice(startMessageCount) + .filter((m: Message) => m.role === 'model') + .map((m: Message) => + m.content + .filter((p) => !!p.toolRequest) + .map( + (p) => + `${p.toolRequest?.name}(${JSON.stringify(p.toolRequest?.input)})` + ) + ) + .flat() + .filter((t: ToolRequestPart) => !!t); + + console.log('\nTools Used:', toolsUsed); +} + +// Main chat loop +async function handleUserInput(chat: any): Promise { + return new Promise((resolve) => { + rl.question(`\n${COLORS.PROMPT}prompt>${COLORS.RESET} `, async (input) => { + try { + const startMessageCount = chat.messages.length; + const { stream, response } = await chat.sendStream(input); + await handleChatResponse(stream, response, startMessageCount); + resolve(); + } catch (e) { + console.log('Error:', e); + resolve(); + } + }); + }); +} + +async function main() { + const chat = ai + .createSession({ initialState: EXAMPLE_USER_CONTEXT }) + .chat(routingAgent); + + const greeting = await getGreeting(); + console.log(); + printColored('bell', greeting, COLORS.BELL); + + while (true) { + await handleUserInput(chat); + } +} + +setTimeout(main, 0); diff --git a/samples/js-schoolAgent/src/tools.ts b/samples/js-schoolAgent/src/tools.ts new file mode 100644 index 000000000..b0e3b848b --- /dev/null +++ b/samples/js-schoolAgent/src/tools.ts @@ -0,0 +1,147 @@ +/** + * Copyright 2024 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 { EXAMPLE_EVENTS, EXAMPLE_GRADES, getUpcomingHolidays } from './data.js'; +import { ai, z } from './genkit.js'; +import { AgentState } from './types.js'; + +export const searchEvents = ai.defineTool( + { + name: 'searchEvents', + description: + 'use this when asked about any time/location for school events including extra curriculars like clubs', + inputSchema: z.object({ + activity: z + .string() + .optional() + .describe( + 'if looking for a particular activity, provide it here. must be an exact match for an activity name' + ), + grade: z + .number() + .optional() + .describe('restrict searched events to a particular grade level'), + }), + }, + async ({ activity, grade }) => { + return EXAMPLE_EVENTS.filter( + (e) => !grade || e.grades.includes(grade) + ).filter( + (e) => !activity || e.activity?.toLowerCase() === activity?.toLowerCase() + ); + } +); + +function checkIsParent(studentId: number, state: AgentState) { + const student = state.students.find((s) => s.id === studentId); + if (!student) { + throw new Error( + `Unable to process request for student ID ${studentId}. Parents can only submit requests for their registered children.` + ); + } + return student; +} + +export const reportAbsence = ai.defineTool( + { + name: 'reportAbsence', + description: + 'use this tool to mark a specific student as absent on one or more days', + inputSchema: z.object({ + studentId: z.number().describe('the id of the student'), + date: z.string().describe('the date of the absence in YYYY-MM-DD format'), + reason: z.string().describe('the provided reason for the absence'), + excused: z + .boolean() + .describe('whether the absence is excused by the parent'), + }), + }, + async (input) => { + const student = checkIsParent( + input.studentId, + ai.currentSession().state! + ); + console.log( + `[TOOL] Absence reported for ${student.name} (ID: ${input.studentId}) on ${input.date}` + ); + return { ok: true, message: 'Absence successfully recorded' }; + } +); + +export const reportTardy = ai.defineTool( + { + name: 'reportTardy', + description: + 'use this tool to mark a specific student tardy for a given date', + inputSchema: z.object({ + studentId: z.number().describe('the id of the student'), + date: z.string().describe('the date of the tardy'), + reason: z.string().describe('the provided reason reason for the tardy'), + eta: z + .string() + .describe( + 'the time the student is expected to arrive at school in HH:MMam/pm format' + ), + excused: z + .boolean() + .describe('whether the absense is excused by the parent'), + }), + }, + async (input) => { + checkIsParent(input.studentId, ai.currentSession().state!); + console.log( + '[TOOL] Student', + input.studentId, + 'has been reported tardy for', + input.date + ); + return { ok: true }; + } +); + +export const upcomingHolidays = ai.defineTool( + { + name: 'upcomingHolidays', + description: 'can retrieve information about upcoming holidays', + outputSchema: z.string(), + }, + async () => JSON.stringify(await getUpcomingHolidays()) +); + +export const getRecentGrades = ai.defineTool( + { + name: 'getRecentGrades', + description: 'retrieves recent grades for a specific student', + inputSchema: z.object({ + studentId: z.number().describe('the id of the student'), + subject: z.string().optional().describe('optional subject filter'), + limit: z + .number() + .optional() + .describe('number of recent grades to return'), + }), + }, + async ({ studentId, subject, limit = 5 }) => { + checkIsParent(studentId, ai.currentSession().state!); + let grades = EXAMPLE_GRADES.filter((g) => g.studentId === studentId); + if (subject) { + grades = grades.filter( + (g) => g.subject.toLowerCase() === subject.toLowerCase() + ); + } + return grades.slice(0, limit); + } +); diff --git a/samples/js-schoolAgent/src/types.ts b/samples/js-schoolAgent/src/types.ts new file mode 100644 index 000000000..542b2351f --- /dev/null +++ b/samples/js-schoolAgent/src/types.ts @@ -0,0 +1,26 @@ +/** + * Copyright 2024 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. + */ + +export interface AgentState { + parentId: number; + parentName: string; + students: { + id: number; + name: string; + grade: number; + activities: string[]; + }[]; +} diff --git a/samples/js-schoolAgent/src/util.ts b/samples/js-schoolAgent/src/util.ts new file mode 100644 index 000000000..e66f628fe --- /dev/null +++ b/samples/js-schoolAgent/src/util.ts @@ -0,0 +1,20 @@ +/** + * Copyright 2024 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. + */ + +export const agentDescription = (specialization: string, tools: string[]) => ` +Transfer to this agent when the user asks about ${specialization}. +This agent can perform the following functions: ${tools.map((t) => t).join(', ')}. +Do not mention that you are transferring, just do it.`; diff --git a/samples/js-schoolAgent/tsconfig.json b/samples/js-schoolAgent/tsconfig.json new file mode 100644 index 000000000..b73ccd04d --- /dev/null +++ b/samples/js-schoolAgent/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", + "noImplicitReturns": true, + "noUnusedLocals": false, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017", + "skipLibCheck": true, + "esModuleInterop": true + }, + "compileOnSave": true, + "include": ["src"] +} diff --git a/samples/prompts/README.md b/samples/prompts/README.md new file mode 100644 index 000000000..35f1a1735 --- /dev/null +++ b/samples/prompts/README.md @@ -0,0 +1,9 @@ +# Prompts Samples + +These sample shows off several of the prompting techniques show in [docs/prompts.md](/docs/prompts.md) + +These examples use Google Gemini, set your API key in the `GOOGLE_GENAI_API_KEY` environment variable. + +You can run these prompts in the Developer UI with `genkit start` + +or invoke them with e.g. `genkit flow:run simplePrompt` diff --git a/samples/prompts/package.json b/samples/prompts/package.json new file mode 100644 index 000000000..081931c36 --- /dev/null +++ b/samples/prompts/package.json @@ -0,0 +1,26 @@ +{ + "name": "prompts", + "version": "1.0.0", + "description": "", + "main": "lib/index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node lib/index.js", + "genkit:dev": "genkit start -- tsx --watch src/index.ts", + "build": "tsc", + "build:watch": "tsc --watch" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "genkit": "^0.9.0-rc || ^0.9", + "@genkit-ai/googleai": "^0.9.0-rc || ^0.9", + "express": "^4.21.0", + "zod": "^3.23.8" + }, + "devDependencies": { + "genkit-cli": "^0.9.0-rc || ^0.9", + "typescript": "^5.5.4" + } +} diff --git a/samples/prompts/src/index.ts b/samples/prompts/src/index.ts new file mode 100644 index 000000000..9149c0358 --- /dev/null +++ b/samples/prompts/src/index.ts @@ -0,0 +1,89 @@ +/** + * Copyright 2024 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 the Genkit core libraries and plugins. +import { googleAI } from '@genkit-ai/googleai'; +import { genkit, z } from 'genkit'; + +const ai = genkit({ + plugins: [ + googleAI(), // Provide the key via the GOOGLE_GENAI_API_KEY environment variable or arg { apiKey: 'yourkey'} + ], +}); + +const simplePrompt = ai.defineFlow('simplePrompt', () => + ai.generate({ + model: 'googleai/gemini-1.5-flash', + prompt: 'You are a helpful AI assistant named Walt, say hello', + }) +); + +const simpleTemplate = ai.defineFlow('simpleTemplate', () => { + const name = 'Fred'; + return ai.generate({ + model: 'googleai/gemini-1.5-flash', + prompt: `You are a helpful AI assistant named Walt. Say hello to ${name}.`, + }); +}); + +const helloDotprompt = ai.definePrompt( + { + name: 'helloPrompt', + model: 'googleai/gemini-1.5-flash', + input: { + schema: z.object({ name: z.string() }), + }, + }, + `You are a helpful AI assistant named Walt. Say hello to {{name}}` +); + +const simpleDotprompt = ai.defineFlow('simpleDotprompt', () => { + return helloDotprompt({ name: 'Fred' }); +}); + +const outputSchema = z.object({ + short: z.string(), + friendly: z.string(), + likeAPirate: z.string(), +}); + +const threeGreetingsPrompt = ai.definePrompt( + { + name: 'threeGreetingsPrompt', + model: 'googleai/gemini-1.5-flash', + input: { + schema: z.object({ name: z.string() }), + }, + output: { + format: 'json', + schema: outputSchema, + }, + }, + `You are a helpful AI assistant named Walt. Say hello to {{name}}, write a response for each of the styles requested` +); + +const threeGreetings = ai.defineFlow('threeGreetingsPrompt', async () => { + const response = await threeGreetingsPrompt({ name: 'Fred' }); + return response.output?.likeAPirate; +}); + +// Start a flow server, which exposes your flows as HTTP endpoints. This call +// must come last, after all of your plug-in configuration and flow definitions. +// You can optionally specify a subset of flows to serve, and configure some +// HTTP server options, but by default, the flow server serves all defined flows. +ai.startFlowServer({ + flows: [threeGreetings, simpleTemplate, simpleDotprompt, simplePrompt], +}); diff --git a/samples/prompts/tsconfig.json b/samples/prompts/tsconfig.json new file mode 100644 index 000000000..efbb566bf --- /dev/null +++ b/samples/prompts/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compileOnSave": true, + "include": ["src"], + "compilerOptions": { + "module": "commonjs", + "noImplicitReturns": true, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017", + "skipLibCheck": true, + "esModuleInterop": true + } +} From 6b541a31975b3575d86bd95b6b101aec9bfa1c81 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Thu, 16 Jan 2025 10:42:32 -0800 Subject: [PATCH 181/562] [chore]: Make TSConfig stricter and fix as many errors as possible (#1596) * Tighten TSConfig; fix new errors * Fix tests; required re-adding ts-node * Checkpoint. Stuck on @types/json-schema * Fix ollama and core * cleanup * Move ts-node back to root * pnpm lock updated * Remove skipLibCheck from firebase; google-cloud still uses outdated long * Add back gablorkenTool * Revert "Remove skipLibCheck from firebase; google-cloud still uses outdated long" This reverts commit 0ba65df5017be29f18db2a44582c0374e88c2b6b. --- js/ai/src/formats/{types.d.ts => types.ts} | 4 +- js/ai/src/generate/action.ts | 20 +- js/ai/src/generate/response.ts | 8 - js/ai/src/model/middleware.ts | 5 +- js/ai/src/testing/model-tester.ts | 6 +- js/ai/tests/formats/array_test.ts | 1 - js/ai/tests/formats/json_test.ts | 4 +- js/core/package.json | 5 +- js/core/src/tracing.ts | 27 +- js/core/tsconfig.json | 6 +- js/genkit/src/client/client.ts | 17 +- js/plugins/chroma/tsconfig.json | 7 +- js/plugins/firebase/tsconfig.json | 9 + .../google-cloud/src/gcpOpenTelemetry.ts | 2 +- js/plugins/google-cloud/tests/metrics_test.ts | 16 +- js/plugins/google-cloud/tsconfig.json | 11 +- js/plugins/langchain/tsconfig.json | 13 +- js/plugins/mcp/src/client/tools.ts | 2 +- js/plugins/mcp/src/index.ts | 2 +- js/plugins/mcp/src/server.ts | 5 +- js/plugins/ollama/package.json | 4 +- js/plugins/ollama/src/embeddings.ts | 2 +- js/plugins/pinecone/tsconfig.json | 8 +- js/plugins/vertexai/src/imagen.ts | 5 - js/plugins/vertexai/src/vectorsearch/index.ts | 3 +- .../vector_search/query_public_endpoint.ts | 2 +- .../vectorsearch/vector_search/retrievers.ts | 1 - js/plugins/vertexai/tsconfig.json | 11 +- js/pnpm-lock.yaml | 281 +++++++++++++++--- js/tsconfig.json | 11 +- pnpm-lock.yaml | 46 +-- 31 files changed, 402 insertions(+), 142 deletions(-) rename js/ai/src/formats/{types.d.ts => types.ts} (88%) diff --git a/js/ai/src/formats/types.d.ts b/js/ai/src/formats/types.ts similarity index 88% rename from js/ai/src/formats/types.d.ts rename to js/ai/src/formats/types.ts index 33595224c..7f0c9fbd5 100644 --- a/js/ai/src/formats/types.d.ts +++ b/js/ai/src/formats/types.ts @@ -19,14 +19,14 @@ import { GenerateResponseChunk } from '../generate.js'; import { Message } from '../message.js'; import { ModelRequest } from '../model.js'; -type OutputContentTypes = 'application/json' | 'text/plain'; +export type OutputContentTypes = 'application/json' | 'text/plain'; export interface Formatter { name: string; config: ModelRequest['output']; handler: (schema?: JSONSchema) => { parseMessage(message: Message): O; - parseChunk?: (chunk: GenerateResponseChunk, cursor?: CC) => CO; + parseChunk?: (chunk: GenerateResponseChunk) => CO; instructions?: string; }; } diff --git a/js/ai/src/generate/action.ts b/js/ai/src/generate/action.ts index 94bf6fdd1..1bf320fae 100644 --- a/js/ai/src/generate/action.ts +++ b/js/ai/src/generate/action.ts @@ -152,17 +152,15 @@ async function generate( streamingCallback ? (chunk: GenerateResponseChunkData) => { // Store accumulated chunk data - if (streamingCallback) { - streamingCallback!( - new GenerateResponseChunk(chunk, { - index: 0, - role: 'model', - previousChunks: accumulatedChunks, - parser: resolvedFormat?.handler(request.output?.schema) - .parseChunk, - }) - ); - } + streamingCallback( + new GenerateResponseChunk(chunk, { + index: 0, + role: 'model', + previousChunks: accumulatedChunks, + parser: resolvedFormat?.handler(request.output?.schema) + .parseChunk, + }) + ); accumulatedChunks.push(chunk); } : undefined, diff --git a/js/ai/src/generate/response.ts b/js/ai/src/generate/response.ts index f476bbea3..41b2981c3 100644 --- a/js/ai/src/generate/response.ts +++ b/js/ai/src/generate/response.ts @@ -73,14 +73,6 @@ export class GenerateResponse implements ModelResponseData { this.request = options?.request; } - private get assertMessage(): Message { - if (!this.message) - throw new Error( - 'Operation could not be completed because the response does not contain a generated message.' - ); - return this.message; - } - /** * Throws an error if the response does not contain valid output. */ diff --git a/js/ai/src/model/middleware.ts b/js/ai/src/model/middleware.ts index 434958dfb..2e5530bc1 100644 --- a/js/ai/src/model/middleware.ts +++ b/js/ai/src/model/middleware.ts @@ -15,7 +15,7 @@ */ import { Document } from '../document.js'; -import { +import type { MediaPart, MessageData, ModelInfo, @@ -129,12 +129,15 @@ export function validateSupport(options: { }; } +// N.B. Figure out why array.findLast isn't available despite setting target +// to ES2022 (Node 16.14.0) function lastUserMessage(messages: MessageData[]) { for (let i = messages.length - 1; i >= 0; i--) { if (messages[i].role === 'user') { return messages[i]; } } + return undefined; } /** diff --git a/js/ai/src/testing/model-tester.ts b/js/ai/src/testing/model-tester.ts index 626a4c3af..a4f50a496 100644 --- a/js/ai/src/testing/model-tester.ts +++ b/js/ai/src/testing/model-tester.ts @@ -53,9 +53,9 @@ const tests: Record = { ], }); - const want = ''; + const want = /plus/i; const got = response.text.trim(); - assert.match(got, /plus/i); + assert.match(got, want); }, history: async (registry: Registry, model: string) => { const resolvedModel = (await registry.lookupAction( @@ -155,7 +155,7 @@ export async function testModels( registry: Registry, models: string[] ): Promise { - const gablorkenTool = defineTool( + defineTool( registry, { name: 'gablorkenTool', diff --git a/js/ai/tests/formats/array_test.ts b/js/ai/tests/formats/array_test.ts index a8abb2eaf..a56752950 100644 --- a/js/ai/tests/formats/array_test.ts +++ b/js/ai/tests/formats/array_test.ts @@ -68,7 +68,6 @@ describe('arrayFormat', () => { it(st.desc, () => { const parser = arrayFormatter.handler(); const chunks: GenerateResponseChunkData[] = []; - let lastCursor = 0; for (const chunk of st.chunks) { const newChunk: GenerateResponseChunkData = { diff --git a/js/ai/tests/formats/json_test.ts b/js/ai/tests/formats/json_test.ts index 464db5639..dff531b73 100644 --- a/js/ai/tests/formats/json_test.ts +++ b/js/ai/tests/formats/json_test.ts @@ -64,7 +64,6 @@ describe('jsonFormat', () => { it(st.desc, () => { const parser = jsonFormatter.handler(); const chunks: GenerateResponseChunkData[] = []; - let lastCursor = ''; for (const chunk of st.chunks) { const newChunk: GenerateResponseChunkData = { @@ -72,8 +71,7 @@ describe('jsonFormat', () => { }; const result = parser.parseChunk!( - new GenerateResponseChunk(newChunk, { previousChunks: [...chunks] }), - lastCursor + new GenerateResponseChunk(newChunk, { previousChunks: [...chunks] }) ); chunks.push(newChunk); diff --git a/js/core/package.json b/js/core/package.json index ae33f7e32..33fc15b99 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -32,11 +32,12 @@ "@opentelemetry/sdk-metrics": "^1.25.0", "@opentelemetry/sdk-node": "^0.52.0", "@opentelemetry/sdk-trace-base": "^1.25.0", + "@types/json-schema": "^7.0.15", "ajv": "^8.12.0", - "body-parser": "^1.20.3", - "cors": "^2.8.5", "ajv-formats": "^3.0.1", "async-mutex": "^0.5.0", + "body-parser": "^1.20.3", + "cors": "^2.8.5", "express": "^4.21.0", "get-port": "^5.1.0", "json-schema": "^0.4.0", diff --git a/js/core/src/tracing.ts b/js/core/src/tracing.ts index 85e096f1e..217019a24 100644 --- a/js/core/src/tracing.ts +++ b/js/core/src/tracing.ts @@ -79,24 +79,17 @@ export async function enableTelemetry( } export async function cleanUpTracing(): Promise { - return new Promise((resolve) => { - if (telemetrySDK) { - // Metrics are not flushed as part of the shutdown operation. If metrics - // are enabled, we need to manually flush them *before* the reader - // receives shutdown order. - const metricFlush = maybeFlushMetrics(); + if (!telemetrySDK) { + return; + } - return metricFlush.then(() => { - return telemetrySDK!.shutdown().then(() => { - logger.debug('OpenTelemetry SDK shut down.'); - telemetrySDK = null; - resolve(); - }); - }); - } else { - resolve(); - } - }); + // Metrics are not flushed as part of the shutdown operation. If metrics + // are enabled, we need to manually flush them *before* the reader + // receives shutdown order. + await maybeFlushMetrics(); + await telemetrySDK.shutdown(); + logger.debug('OpenTelemetry SDK shut down.'); + telemetrySDK = null; } /** diff --git a/js/core/tsconfig.json b/js/core/tsconfig.json index 3cf966d7c..d96b77bbb 100644 --- a/js/core/tsconfig.json +++ b/js/core/tsconfig.json @@ -1,4 +1,8 @@ { "extends": "../tsconfig.json", - "include": ["src"] + "include": ["src"], + "compilerOptions": { + // DOM is needed for streaming APIs. + "lib": ["es2022", "dom"] + } } diff --git a/js/genkit/src/client/client.ts b/js/genkit/src/client/client.ts index d9144e1e1..99ba6e6e2 100644 --- a/js/genkit/src/client/client.ts +++ b/js/genkit/src/client/client.ts @@ -194,6 +194,19 @@ export async function runFlow({ `Server returned: ${response.status}: ${await response.text()}` ); } - const wrappedDesult = await response.json(); - return wrappedDesult.result; + const wrappedResult = (await response.json()) as + | { result: O } + | { error: unknown }; + if ('error' in wrappedResult) { + if (typeof wrappedResult.error === 'string') { + throw new Error(wrappedResult.error); + } + // TODO: The callable protocol defines an HttpError that has a JSON format of + // details?: string + // httpErrorCode: { canonicalName: string } + // message: string + // Should we create a new error class that parses this and exposes it as fields? + throw new Error(JSON.stringify(wrappedResult.error)); + } + return wrappedResult.result; } diff --git a/js/plugins/chroma/tsconfig.json b/js/plugins/chroma/tsconfig.json index 596e2cf72..3949a1ba5 100644 --- a/js/plugins/chroma/tsconfig.json +++ b/js/plugins/chroma/tsconfig.json @@ -1,4 +1,9 @@ { "extends": "../../tsconfig.json", - "include": ["src"] + "include": ["src"], + "compilerOptions": { + // chromadb does not compile cleany. It tries to import @xenova/transformers and + // chromadb-default-embed, which do not seem to be packaged with the library + "skipLibCheck": true + } } diff --git a/js/plugins/firebase/tsconfig.json b/js/plugins/firebase/tsconfig.json index 596e2cf72..aece3c102 100644 --- a/js/plugins/firebase/tsconfig.json +++ b/js/plugins/firebase/tsconfig.json @@ -1,4 +1,13 @@ { "extends": "../../tsconfig.json", + "compilerOptions": { + // Google protobuf libraries have a transitive dependency on the long + // library, which incorrectly implements the ESM/CSM dual mode. This + // started in 5.0.0 and protobufjs requires ^5.0.0. + // This can be removed if https://github.com/dcodeIO/long.js/pull/130 gets + // approved, though it's been sitting around for over a year without + // acceptance. + "skipLibCheck": true + }, "include": ["src"] } diff --git a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts index 096e077d7..d543d66a8 100644 --- a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts +++ b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts @@ -219,7 +219,7 @@ export class GcpOpenTelemetry { */ class MetricExporterWrapper extends MetricExporter { constructor( - private options?: ExporterOptions, + options?: ExporterOptions, private errorHandler?: (error: Error) => void ) { super(options); diff --git a/js/plugins/google-cloud/tests/metrics_test.ts b/js/plugins/google-cloud/tests/metrics_test.ts index 190886337..8bba5fcce 100644 --- a/js/plugins/google-cloud/tests/metrics_test.ts +++ b/js/plugins/google-cloud/tests/metrics_test.ts @@ -243,7 +243,7 @@ describe('GoogleCloudMetrics', () => { await ai.generate({ model: testModel, prompt: 'Hi' }); await ai.generate({ model: testModel, prompt: 'Yo' }); - const spans = await getExportedSpans(); + await getExportedSpans(); const requestCounter = await getCounterMetric('genkit/feature/requests'); const latencyHistogram = await getHistogramMetric('genkit/feature/latency'); @@ -544,7 +544,7 @@ describe('GoogleCloudMetrics', () => { it('writes path metrics for a failing flow with exception in root', async () => { const flow = createFlow(ai, 'testFlow', async () => { - const subPath = await ai.run('sub-action', async () => { + await ai.run('sub-action', async () => { return 'done'; }); return Promise.reject(new Error('failed')); @@ -582,8 +582,8 @@ describe('GoogleCloudMetrics', () => { it('writes path metrics for a failing flow with exception in subaction', async () => { const flow = createFlow(ai, 'testFlow', async () => { - const subPath1 = await ai.run('sub-action-1', async () => { - const subPath2 = await ai.run('sub-action-2', async () => { + await ai.run('sub-action-1', async () => { + await ai.run('sub-action-2', async () => { return Promise.reject(new Error('failed')); }); return 'done'; @@ -627,8 +627,8 @@ describe('GoogleCloudMetrics', () => { it('writes path metrics for a flow with exception in action', async () => { const flow = createFlow(ai, 'testFlow', async () => { - const subPath1 = await ai.run('sub-action-1', async () => { - const subPath2 = await ai.run('sub-action-2', async () => { + await ai.run('sub-action-1', async () => { + await ai.run('sub-action-2', async () => { return 'done'; }); return Promise.reject(new Error('failed')); @@ -674,10 +674,10 @@ describe('GoogleCloudMetrics', () => { it('writes path metrics for a flow with an exception in a serial action', async () => { const flow = createFlow(ai, 'testFlow', async () => { - const subPath1 = await ai.run('sub-action-1', async () => { + await ai.run('sub-action-1', async () => { return 'done'; }); - const subPath2 = await ai.run('sub-action-2', async () => { + await ai.run('sub-action-2', async () => { return Promise.reject(new Error('failed')); }); return 'done'; diff --git a/js/plugins/google-cloud/tsconfig.json b/js/plugins/google-cloud/tsconfig.json index 596e2cf72..9b6c8b4e2 100644 --- a/js/plugins/google-cloud/tsconfig.json +++ b/js/plugins/google-cloud/tsconfig.json @@ -1,4 +1,13 @@ { "extends": "../../tsconfig.json", - "include": ["src"] + "include": ["src"], + "compilerOptions": { + // Google protobuf libraries have a transitive dependency on the long + // library, which incorrectly implements the ESM/CSM dual mode. This + // started in 5.0.0 and protobufjs requires ^5.0.0. + // This can be removed if https://github.com/dcodeIO/long.js/pull/130 gets + // approved, though it's been sitting around for over a year without + // acceptance. + "skipLibCheck": true + } } diff --git a/js/plugins/langchain/tsconfig.json b/js/plugins/langchain/tsconfig.json index 596e2cf72..cf52d2741 100644 --- a/js/plugins/langchain/tsconfig.json +++ b/js/plugins/langchain/tsconfig.json @@ -1,4 +1,15 @@ { "extends": "../../tsconfig.json", - "include": ["src"] + "include": ["src"], + "compilerOptions": { + // The langchain+core package has multiple ts errors: + // 1. In chat_models.d.ts and llms.d.ts, TypeScript cannot infer the type of `this`. + // this error might be resolved by upgrading typescript versions + // 2. outputs.d.cts and base.d.cts mix up ESM and CSM and are using the wrong loader. + // + // The langchain package also has errors: + // 1. callbacks.d.ts, evaluation.d.cts, and base.d.cts mix up ESM and CSM and are + // using the wrong loader + "skipLibCheck": true + } } diff --git a/js/plugins/mcp/src/client/tools.ts b/js/plugins/mcp/src/client/tools.ts index 433f5c135..06a25100f 100644 --- a/js/plugins/mcp/src/client/tools.ts +++ b/js/plugins/mcp/src/client/tools.ts @@ -56,7 +56,7 @@ function registerTool( { name: `${params.name}/${tool.name}`, description: tool.description || '', - inputJsonSchema: tool.inputSchema, + inputJsonSchema: tool.inputSchema.properties, outputSchema: z.any(), }, async (args) => { diff --git a/js/plugins/mcp/src/index.ts b/js/plugins/mcp/src/index.ts index f5bf3e830..39e21b5c3 100644 --- a/js/plugins/mcp/src/index.ts +++ b/js/plugins/mcp/src/index.ts @@ -44,7 +44,7 @@ async function transportFrom(params: McpClientOptions): Promise { const { SSEClientTransport } = await import( '@modelcontextprotocol/sdk/client/sse.js' ); - return new SSEClientTransport(URL.parse(params.serverUrl)!); + return new SSEClientTransport(new URL(params.serverUrl)); } if (params.serverProcess) { const { StdioClientTransport } = await import( diff --git a/js/plugins/mcp/src/server.ts b/js/plugins/mcp/src/server.ts index 5acb0c451..5fdfd8fc9 100644 --- a/js/plugins/mcp/src/server.ts +++ b/js/plugins/mcp/src/server.ts @@ -184,7 +184,9 @@ export class GenkitMcpServer { } } -function toMcpPromptArguments(p: PromptAction): Prompt['arguments'] { +function toMcpPromptArguments( + p: PromptAction +): Prompt['arguments'] | undefined { const jsonSchema = toJsonSchema({ schema: p.__action.inputSchema, jsonSchema: p.__action.inputJsonSchema, @@ -216,6 +218,7 @@ function toMcpPromptArguments(p: PromptAction): Prompt['arguments'] { required: jsonSchema.required?.includes(k), }); } + return args; } const ROLE_MAP = { model: 'assistant', user: 'user' } as const; diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index a7747a986..97547108b 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -31,10 +31,12 @@ "peerDependencies": { "genkit": "workspace:*" }, + "dependencies": { + "ollama": "^0.5.9" + }, "devDependencies": { "@types/node": "^20.11.16", "npm-run-all": "^4.1.5", - "ollama": "^0.5.9", "rimraf": "^6.0.1", "tsup": "^8.3.5", "tsx": "^4.19.2", diff --git a/js/plugins/ollama/src/embeddings.ts b/js/plugins/ollama/src/embeddings.ts index a559c62f9..6d4dc771a 100644 --- a/js/plugins/ollama/src/embeddings.ts +++ b/js/plugins/ollama/src/embeddings.ts @@ -98,7 +98,7 @@ export function defineOllamaEmbedder( ); } - const payload: EmbedResponse = await response.json(); + const payload = (await response.json()) as EmbedResponse; const embeddings: { embedding: number[] }[] = []; diff --git a/js/plugins/pinecone/tsconfig.json b/js/plugins/pinecone/tsconfig.json index 596e2cf72..a255d005d 100644 --- a/js/plugins/pinecone/tsconfig.json +++ b/js/plugins/pinecone/tsconfig.json @@ -1,4 +1,10 @@ { "extends": "../../tsconfig.json", - "include": ["src"] + "include": ["src"], + "compilerOptions": { + // The @pinecone-database+pinecone library has TypeScript errors: + // 1. There are three errors that the type RequestCredentials cannot be found + // 2. There is one error that WindowOrWorkerGlobalScope cannot be found + "skipLibCheck": true + } } diff --git a/js/plugins/vertexai/src/imagen.ts b/js/plugins/vertexai/src/imagen.ts index 926a9d24d..3b71ba107 100644 --- a/js/plugins/vertexai/src/imagen.ts +++ b/js/plugins/vertexai/src/imagen.ts @@ -269,11 +269,6 @@ export function imagenModel( }; } - const req: any = { - instances: [instance], - parameters: toParameters(request), - }; - const predictClient = predictClientFactory(request); const response = await predictClient([instance], toParameters(request)); diff --git a/js/plugins/vertexai/src/vectorsearch/index.ts b/js/plugins/vertexai/src/vectorsearch/index.ts index e372660e5..1a8ddca75 100644 --- a/js/plugins/vertexai/src/vectorsearch/index.ts +++ b/js/plugins/vertexai/src/vectorsearch/index.ts @@ -39,8 +39,7 @@ export { */ export function vertexAIVectorSearch(options?: PluginOptions): GenkitPlugin { return genkitPlugin('vertexAIVectorSearch', async (ai: Genkit) => { - const { projectId, location, vertexClientFactory, authClient } = - await getDerivedParams(options); + const { authClient } = await getDerivedParams(options); if ( options?.vectorSearchOptions && diff --git a/js/plugins/vertexai/src/vectorsearch/vector_search/query_public_endpoint.ts b/js/plugins/vertexai/src/vectorsearch/vector_search/query_public_endpoint.ts index f055e3b9d..eaa2bc30e 100644 --- a/js/plugins/vertexai/src/vectorsearch/vector_search/query_public_endpoint.ts +++ b/js/plugins/vertexai/src/vectorsearch/vector_search/query_public_endpoint.ts @@ -88,5 +88,5 @@ export async function queryPublicEndpoint( if (!response.ok) { logger.error('Error querying index: ', response.statusText); } - return response.json(); + return response.json() as FindNeighborsResponse; } diff --git a/js/plugins/vertexai/src/vectorsearch/vector_search/retrievers.ts b/js/plugins/vertexai/src/vectorsearch/vector_search/retrievers.ts index 0f8a64024..ca9975a40 100644 --- a/js/plugins/vertexai/src/vectorsearch/vector_search/retrievers.ts +++ b/js/plugins/vertexai/src/vectorsearch/vector_search/retrievers.ts @@ -48,7 +48,6 @@ export function vertexAiRetrievers( for (const vectorSearchOption of vectorSearchOptions) { const { documentRetriever, indexId, publicDomainName } = vectorSearchOption; - const embedder = vectorSearchOption.embedder ?? defaultEmbedder; const embedderOptions = vectorSearchOption.embedderOptions; const retriever = ai.defineRetriever( diff --git a/js/plugins/vertexai/tsconfig.json b/js/plugins/vertexai/tsconfig.json index 596e2cf72..9b6c8b4e2 100644 --- a/js/plugins/vertexai/tsconfig.json +++ b/js/plugins/vertexai/tsconfig.json @@ -1,4 +1,13 @@ { "extends": "../../tsconfig.json", - "include": ["src"] + "include": ["src"], + "compilerOptions": { + // Google protobuf libraries have a transitive dependency on the long + // library, which incorrectly implements the ESM/CSM dual mode. This + // started in 5.0.0 and protobufjs requires ^5.0.0. + // This can be removed if https://github.com/dcodeIO/long.js/pull/130 gets + // approved, though it's been sitting around for over a year without + // acceptance. + "skipLibCheck": true + } } diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index d7aab53af..2db426433 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -99,6 +99,9 @@ importers: '@opentelemetry/sdk-trace-base': specifier: ^1.25.0 version: 1.25.1(@opentelemetry/api@1.9.0) + '@types/json-schema': + specifier: ^7.0.15 + version: 7.0.15 ajv: specifier: ^8.12.0 version: 8.12.0 @@ -492,7 +495,7 @@ importers: version: 5.1.0 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.11.30) + version: 29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)) npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -501,7 +504,7 @@ importers: version: 6.0.1 ts-jest: specifier: ^29.1.2 - version: 29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.11.30))(typescript@4.9.5) + version: 29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)))(typescript@4.9.5) tsup: specifier: ^8.3.5 version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) @@ -577,7 +580,7 @@ importers: version: 20.11.30 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.11.30) + version: 29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)) npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -586,7 +589,7 @@ importers: version: 6.0.1 ts-jest: specifier: ^29.1.2 - version: 29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.11.30))(typescript@4.9.5) + version: 29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)))(typescript@4.9.5) tsup: specifier: ^8.3.5 version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) @@ -688,7 +691,7 @@ importers: version: 20.16.9 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.16.9) + version: 29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)) npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -697,7 +700,7 @@ importers: version: 6.0.1 ts-jest: specifier: ^29.1.2 - version: 29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.16.9))(typescript@5.6.3) + version: 29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)))(typescript@5.6.3) tsup: specifier: ^8.3.5 version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.6.1) @@ -713,6 +716,9 @@ importers: genkit: specifier: workspace:* version: link:../../genkit + ollama: + specifier: ^0.5.9 + version: 0.5.9 devDependencies: '@types/node': specifier: ^20.11.16 @@ -720,9 +726,6 @@ importers: npm-run-all: specifier: ^4.1.5 version: 4.1.5 - ollama: - specifier: ^0.5.9 - version: 0.5.9 rimraf: specifier: ^6.0.1 version: 6.0.1 @@ -1867,6 +1870,10 @@ packages: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + '@dabh/diagnostics@2.0.3': resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} @@ -2619,6 +2626,9 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@js-sdsl/ordered-map@4.4.2': resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} @@ -3517,6 +3527,18 @@ packages: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@types/aws-lambda@8.10.122': resolution: {integrity: sha512-vBkIh9AY22kVOCEKo5CJlyCgmSWvasC+SWUxL/x/vOwRobMpI/HG1xp/Ae3AqmSiZeLUbOhW0FCD3ZjqqUxmXw==} @@ -3586,6 +3608,9 @@ packages: '@types/jest@29.5.13': resolution: {integrity: sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/jsonwebtoken@9.0.6': resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} @@ -3695,6 +3720,10 @@ packages: peerDependencies: acorn: ^8 + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + acorn@8.11.3: resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} @@ -3758,6 +3787,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -4075,6 +4107,9 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + cross-env@7.0.3: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} @@ -4195,6 +4230,10 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + digest-fetch@1.3.0: resolution: {integrity: sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==} @@ -6322,6 +6361,20 @@ packages: resolution: {integrity: sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==} engines: {node: '>=12'} + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} @@ -6489,6 +6542,9 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + v8-to-istanbul@9.3.0: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} @@ -6629,6 +6685,10 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -6889,6 +6949,11 @@ snapshots: '@colors/colors@1.6.0': {} + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + optional: true + '@dabh/diagnostics@2.0.3': dependencies: colorspace: 1.1.4 @@ -7684,7 +7749,7 @@ snapshots: jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0': + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -7698,7 +7763,42 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.16.9) + jest-config: 29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3))': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.16.9 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -7854,6 +7954,12 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + optional: true + '@js-sdsl/ordered-map@4.4.2': {} '@langchain/community@0.0.53(@pinecone-database/pinecone@2.2.0)(chromadb@1.9.2(encoding@0.1.13)(openai@4.53.0(encoding@0.1.13)))(encoding@0.1.13)(firebase-admin@12.3.1(encoding@0.1.13))(google-auth-library@8.9.0(encoding@0.1.13))(jsonwebtoken@9.0.2)': @@ -8665,6 +8771,18 @@ snapshots: '@tootallnate/once@2.0.0': {} + '@tsconfig/node10@1.0.11': + optional: true + + '@tsconfig/node12@1.0.11': + optional: true + + '@tsconfig/node14@1.0.3': + optional: true + + '@tsconfig/node16@1.0.4': + optional: true + '@types/aws-lambda@8.10.122': {} '@types/babel__core@7.20.5': @@ -8763,6 +8881,8 @@ snapshots: expect: 29.7.0 pretty-format: 29.7.0 + '@types/json-schema@7.0.15': {} + '@types/jsonwebtoken@9.0.6': dependencies: '@types/node': 20.16.9 @@ -8881,6 +9001,11 @@ snapshots: dependencies: acorn: 8.11.3 + acorn-walk@8.3.4: + dependencies: + acorn: 8.11.3 + optional: true + acorn@8.11.3: {} agent-base@6.0.2: @@ -8937,6 +9062,9 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + arg@4.1.3: + optional: true + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -9277,13 +9405,13 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 - create-jest@29.7.0(@types/node@20.11.30): + create-jest@29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.11.30) + jest-config: 29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -9292,13 +9420,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@20.16.9): + create-jest@29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.16.9) + jest-config: 29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -9307,6 +9435,9 @@ snapshots: - supports-color - ts-node + create-require@1.1.1: + optional: true + cross-env@7.0.3: dependencies: cross-spawn: 7.0.6 @@ -9406,6 +9537,9 @@ snapshots: diff-sequences@29.6.3: {} + diff@4.0.2: + optional: true + digest-fetch@1.3.0: dependencies: base-64: 0.1.0 @@ -10489,16 +10623,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@20.11.30): + jest-cli@29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.11.30) + create-jest: 29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@20.11.30) + jest-config: 29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -10508,16 +10642,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@20.16.9): + jest-cli@29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.16.9) + create-jest: 29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@20.16.9) + jest-config: 29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -10527,7 +10661,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@20.11.30): + jest-config@29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)): dependencies: '@babel/core': 7.25.7 '@jest/test-sequencer': 29.7.0 @@ -10553,11 +10687,12 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.11.30 + ts-node: 10.9.2(@types/node@20.11.30)(typescript@4.9.5) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.16.9): + jest-config@29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)): dependencies: '@babel/core': 7.25.7 '@jest/test-sequencer': 29.7.0 @@ -10583,6 +10718,38 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.16.9 + ts-node: 10.9.2(@types/node@20.11.30)(typescript@4.9.5) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-config@29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)): + dependencies: + '@babel/core': 7.25.7 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.25.7) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.16.9 + ts-node: 10.9.2(@types/node@20.16.9)(typescript@5.6.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -10802,24 +10969,24 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@20.11.30): + jest@29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@20.11.30) + jest-cli: 29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros - supports-color - ts-node - jest@29.7.0(@types/node@20.16.9): + jest@29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@20.16.9) + jest-cli: 29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -12094,12 +12261,12 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.11.30))(typescript@4.9.5): + ts-jest@29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)))(typescript@4.9.5): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.11.30) + jest: 29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -12113,12 +12280,12 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.25.7) - ts-jest@29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.16.9))(typescript@5.6.3): + ts-jest@29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)))(typescript@5.6.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.16.9) + jest: 29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -12134,6 +12301,44 @@ snapshots: ts-md5@1.3.1: {} + ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.11.30 + acorn: 8.11.3 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 4.9.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optional: true + + ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.16.9 + acorn: 8.11.3 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.6.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optional: true + tslib@2.6.2: {} tsup@8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.4.1): @@ -12352,6 +12557,9 @@ snapshots: uuid@9.0.1: {} + v8-compile-cache-lib@3.0.1: + optional: true + v8-to-istanbul@9.3.0: dependencies: '@jridgewell/trace-mapping': 0.3.25 @@ -12500,6 +12708,9 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yn@3.1.1: + optional: true + yocto-queue@0.1.0: {} zod-to-json-schema@3.22.5(zod@3.23.8): diff --git a/js/tsconfig.json b/js/tsconfig.json index 0b00e340d..f97e2100c 100644 --- a/js/tsconfig.json +++ b/js/tsconfig.json @@ -3,17 +3,16 @@ "declaration": true, "module": "NodeNext", "moduleResolution": "NodeNext", - "noImplicitReturns": false, - "noUnusedLocals": false, + "allowSyntheticDefaultImports": true, + "noImplicitReturns": true, + "noUnusedLocals": true, "noImplicitAny": false, "resolveJsonModule": true, "sourceMap": true, "esModuleInterop": true, "strict": true, - "target": "es2015", - "allowSyntheticDefaultImports": true, - "skipLibCheck": true, - "typeRoots": ["./node_modules/@types"], + "target": "es2022", + "lib": ["es2022"], "noEmit": true }, "compileOnSave": true diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f00f333a9..52702ed5b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,7 +37,7 @@ importers: version: 6.0.1 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@20.12.11)(typescript@5.4.5) + version: 10.9.2(@types/node@22.10.5)(typescript@5.4.5) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -200,8 +200,8 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.4.15': - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -218,15 +218,15 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - '@types/node@20.12.11': - resolution: {integrity: sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==} + '@types/node@22.10.5': + resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} - acorn-walk@8.3.2: - resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} - acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} hasBin: true @@ -1013,8 +1013,8 @@ packages: unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -1153,12 +1153,12 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@tsconfig/node10@1.0.11': {} @@ -1168,13 +1168,15 @@ snapshots: '@tsconfig/node16@1.0.4': {} - '@types/node@20.12.11': + '@types/node@22.10.5': dependencies: - undici-types: 5.26.5 + undici-types: 6.20.0 - acorn-walk@8.3.2: {} + acorn-walk@8.3.4: + dependencies: + acorn: 8.14.0 - acorn@8.11.3: {} + acorn@8.14.0: {} ansi-escapes@4.3.2: dependencies: @@ -2018,16 +2020,16 @@ snapshots: dependencies: os-tmpdir: 1.0.2 - ts-node@10.9.2(@types/node@20.12.11)(typescript@5.4.5): + ts-node@10.9.2(@types/node@22.10.5)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.12.11 - acorn: 8.11.3 - acorn-walk: 8.3.2 + '@types/node': 22.10.5 + acorn: 8.14.0 + acorn-walk: 8.3.4 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 @@ -2090,7 +2092,7 @@ snapshots: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 - undici-types@5.26.5: {} + undici-types@6.20.0: {} util-deprecate@1.0.2: {} From 51ebf4e910c88de13aa27487141bc6108a3d7e01 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 16 Jan 2025 16:25:08 -0500 Subject: [PATCH 182/562] fix(js/ai): skip response schema validation on tool calls (#1597) --- js/ai/src/generate/action.ts | 5 ++- js/ai/src/generate/response.ts | 10 ++++- js/ai/tests/generate/response_test.ts | 4 +- js/genkit/tests/generate_test.ts | 55 +++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 5 deletions(-) diff --git a/js/ai/src/generate/action.ts b/js/ai/src/generate/action.ts index 1bf320fae..675c8be3a 100644 --- a/js/ai/src/generate/action.ts +++ b/js/ai/src/generate/action.ts @@ -188,11 +188,14 @@ async function generate( ); // Throw an error if the response is not usable. - response.assertValid(request); + response.assertValid(); const message = response.message!; // would have thrown if no message const toolCalls = message.content.filter((part) => !!part.toolRequest); if (rawRequest.returnToolRequests || toolCalls.length === 0) { + if (toolCalls.length === 0) { + response.assertValidSchema(request); + } return response.toJSON(); } const maxIterations = rawRequest.maxTurns ?? 5; diff --git a/js/ai/src/generate/response.ts b/js/ai/src/generate/response.ts index 41b2981c3..0a31d5338 100644 --- a/js/ai/src/generate/response.ts +++ b/js/ai/src/generate/response.ts @@ -76,7 +76,7 @@ export class GenerateResponse implements ModelResponseData { /** * Throws an error if the response does not contain valid output. */ - assertValid(request?: GenerateRequest): void { + assertValid(): void { if (this.finishReason === 'blocked') { throw new GenerationBlockedError( this, @@ -90,7 +90,12 @@ export class GenerateResponse implements ModelResponseData { `Model did not generate a message. Finish reason: '${this.finishReason}': ${this.finishMessage}` ); } + } + /** + * Throws an error if the response does not conform to expected schema. + */ + assertValidSchema(request?: GenerateRequest): void { if (request?.output?.schema || this.request?.output?.schema) { const o = this.output; parseSchema(o, { @@ -101,7 +106,8 @@ export class GenerateResponse implements ModelResponseData { isValid(request?: GenerateRequest): boolean { try { - this.assertValid(request); + this.assertValid(); + this.assertValidSchema(request); return true; } catch (e) { return false; diff --git a/js/ai/tests/generate/response_test.ts b/js/ai/tests/generate/response_test.ts index 698795385..6689a3ee1 100644 --- a/js/ai/tests/generate/response_test.ts +++ b/js/ai/tests/generate/response_test.ts @@ -156,7 +156,7 @@ describe('GenerateResponse', () => { assert.throws( () => { - response.assertValid(request); + response.assertValidSchema(request); }, (err: unknown) => { return err instanceof Error && err.message.includes('must be number'); @@ -186,7 +186,7 @@ describe('GenerateResponse', () => { }; assert.doesNotThrow(() => { - response.assertValid(request); + response.assertValidSchema(request); }); }); }); diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index 653b96f00..3120b458e 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { z } from '@genkit-ai/core'; import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; import { modelRef } from '../../ai/src/model'; @@ -359,6 +360,60 @@ describe('generate', () => { ); }); + it('call the tool with output schema', async () => { + const schema = z.object({ + foo: z.string(), + }); + + ai.defineTool( + { + name: 'testTool', + description: 'description', + inputSchema: schema, + outputSchema: schema, + }, + async () => { + return { + foo: 'bar', + }; + } + ); + + // first response be tools call, the subsequent just text response from agent b. + let reqCounter = 0; + pm.handleResponse = async (req, sc) => { + return { + message: { + role: 'model', + content: [ + reqCounter++ === 0 + ? { + toolRequest: { + name: 'testTool', + input: { foo: 'fromTool' }, + ref: 'ref123', + }, + } + : { + text: "```\n{foo: 'fromModel'}\n```", + }, + ], + }, + }; + }; + + const { text, output } = await ai.generate({ + output: { schema }, + prompt: 'call the tool', + tools: ['testTool'], + }); + + assert.strictEqual(text, "```\n{foo: 'fromModel'}\n```"); + assert.deepStrictEqual(output, { + foo: 'fromModel', + }); + }); + it('throws when exceeding max tool call iterations', async () => { ai.defineTool( { name: 'testTool', description: 'description' }, From 7d22c8b2cb7266f6986a6e19975b150c6b4cafcd Mon Sep 17 00:00:00 2001 From: thedmail Date: Thu, 16 Jan 2025 13:32:17 -0800 Subject: [PATCH 183/562] Update README.md (#1616) Docs: Restores the original Samples links since the rollback of their migration. Also adds links to two samples that hadn't been mentioned here. --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 84a4bef37..d27e60804 100644 --- a/README.md +++ b/README.md @@ -101,11 +101,13 @@ Want to skip the local setup? Click below to try out Genkit using [Project IDX]( Take a look at some samples of Genkit in use: -- ["AI barista"](https://github.com/firebase/quickstart-nodejs/tree/master/genkit/js-coffee-shop) -- demonstrates simple LLM usage -- [A simple chatbot with a JavaScript frontend](https://github.com/firebase/quickstart-nodejs/tree/master/genkit/chatbot) -- add history to LLM sessions -- [Restaurant menu Q&A app](https://github.com/firebase/quickstart-nodejs/tree/master/genkit/js-menu) -- this sample shows progressively +- ["AI barista"](https://github.com/firebase/genkit/blob/main/samples/js-coffee-shop) -- demonstrates simple LLM usage +- [A simple chatbot with a JavaScript frontend](https://github.com/firebase/genkit/blob/main/samples/chatbot) -- add history to LLM sessions +- [Restaurant menu Q&A app](https://github.com/firebase/genkit/blob/main/samples/js-menu) -- this sample shows progressively more sophisticated versions of a menu understanding app. -- [Streaming to an Angular frontend](https://github.com/firebase/quickstart-nodejs/tree/master/genkit/js-angular) +- [Streaming to an Angular frontend](https://github.com/firebase/genkit/blob/main/samples/js-angular) +- [js-schoolAgent](https://github.com/firebase/genkit/blob/main/samples/js-schoolAgent/): A simple school assistant system with a routing agent and specialized agents +- [Prompts](https://github.com/firebase/genkit/blob/main/samples/prompts/): Shows off several prompting techniques ## Connect with us From d91868114bfbe2ceb29733c0f962729265619bf5 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 16 Jan 2025 16:43:51 -0500 Subject: [PATCH 184/562] feat(js/ai): stream tools responses (#1614) --- genkit-tools/common/src/types/model.ts | 8 +-- genkit-tools/genkit-schema.json | 18 +++-- js/ai/src/generate/action.ts | 53 ++++++++++---- js/ai/src/generate/chunk.ts | 28 +++++--- js/ai/src/model.ts | 8 +-- js/ai/tests/formats/array_test.ts | 8 ++- js/ai/tests/formats/json_test.ts | 8 ++- js/ai/tests/formats/jsonl_test.ts | 8 ++- js/ai/tests/formats/text_test.ts | 8 ++- js/ai/tests/generate/chunk_test.ts | 6 +- js/ai/tests/generate/generate_test.ts | 19 +++-- js/genkit/tests/generate_test.ts | 90 +++++++++++++++++++++++- js/plugins/express/tests/express_test.ts | 12 ++-- js/testapps/flow-simple-ai/src/index.ts | 3 +- 14 files changed, 221 insertions(+), 56 deletions(-) diff --git a/genkit-tools/common/src/types/model.ts b/genkit-tools/common/src/types/model.ts index 5ce711c17..d3f218ff4 100644 --- a/genkit-tools/common/src/types/model.ts +++ b/genkit-tools/common/src/types/model.ts @@ -245,6 +245,9 @@ export type GenerateResponseData = z.infer; /** ModelResponseChunkSchema represents a chunk of content to stream to the client. */ export const ModelResponseChunkSchema = z.object({ + role: RoleSchema.optional(), + /** index of the message this chunk belongs to. */ + index: z.number().optional(), /** The chunk of content to stream right now. */ content: z.array(PartSchema), /** Model-specific extra information attached to this chunk. */ @@ -254,10 +257,7 @@ export const ModelResponseChunkSchema = z.object({ }); export type ModelResponseChunkData = z.infer; -export const GenerateResponseChunkSchema = ModelResponseChunkSchema.extend({ - /** @deprecated The index of the candidate this chunk belongs to. Always 0. */ - index: z.number(), -}); +export const GenerateResponseChunkSchema = ModelResponseChunkSchema.extend({}); export type GenerateResponseChunkData = z.infer< typeof GenerateResponseChunkSchema >; diff --git a/genkit-tools/genkit-schema.json b/genkit-tools/genkit-schema.json index 9d629ed14..d6f36262f 100644 --- a/genkit-tools/genkit-schema.json +++ b/genkit-tools/genkit-schema.json @@ -452,6 +452,12 @@ "GenerateResponseChunk": { "type": "object", "properties": { + "role": { + "$ref": "#/$defs/Role" + }, + "index": { + "type": "number" + }, "content": { "type": "array", "items": { @@ -461,14 +467,10 @@ "custom": {}, "aggregated": { "type": "boolean" - }, - "index": { - "type": "number" } }, "required": [ - "content", - "index" + "content" ], "additionalProperties": false }, @@ -714,6 +716,12 @@ "ModelResponseChunk": { "type": "object", "properties": { + "role": { + "$ref": "#/$defs/GenerateResponseChunk/properties/role" + }, + "index": { + "$ref": "#/$defs/GenerateResponseChunk/properties/index" + }, "content": { "$ref": "#/$defs/GenerateResponseChunk/properties/content" }, diff --git a/js/ai/src/generate/action.ts b/js/ai/src/generate/action.ts index 675c8be3a..8a31a87bb 100644 --- a/js/ai/src/generate/action.ts +++ b/js/ai/src/generate/action.ts @@ -83,9 +83,11 @@ export async function generateHelper( registry: Registry, input: z.infer, middleware?: ModelMiddleware[], - currentTurns?: number + currentTurns?: number, + messageIndex?: number ): Promise { currentTurns = currentTurns ?? 0; + messageIndex = messageIndex ?? 0; // do tracing return await runInNewSpan( registry, @@ -100,7 +102,13 @@ export async function generateHelper( async (metadata) => { metadata.name = 'generate'; metadata.input = input; - const output = await generate(registry, input, middleware, currentTurns!); + const output = await generate( + registry, + input, + middleware, + currentTurns!, + messageIndex! + ); metadata.output = JSON.stringify(output); return output; } @@ -111,7 +119,8 @@ async function generate( registry: Registry, rawRequest: z.infer, middleware: ModelMiddleware[] | undefined, - currentTurn: number + currentTurn: number, + messageIndex: number ): Promise { const { modelAction: model } = await resolveModel(registry, rawRequest.model); if (model.__action.metadata?.model.stage === 'deprecated') { @@ -152,15 +161,17 @@ async function generate( streamingCallback ? (chunk: GenerateResponseChunkData) => { // Store accumulated chunk data - streamingCallback( - new GenerateResponseChunk(chunk, { - index: 0, - role: 'model', - previousChunks: accumulatedChunks, - parser: resolvedFormat?.handler(request.output?.schema) - .parseChunk, - }) - ); + if (streamingCallback) { + streamingCallback!( + new GenerateResponseChunk(chunk, { + index: messageIndex, + role: 'model', + previousChunks: accumulatedChunks, + parser: resolvedFormat?.handler(request.output?.schema) + .parseChunk, + }) + ); + } accumulatedChunks.push(chunk); } : undefined, @@ -246,6 +257,7 @@ async function generate( }); } } + messageIndex++; const nextRequest = { ...rawRequest, messages: [ @@ -257,11 +269,26 @@ async function generate( ] as MessageData[], tools: newTools, }; + // stream out the tool responses + streamingCallback?.( + new GenerateResponseChunk( + { + content: toolResponses, + }, + { + index: messageIndex, + role: 'model', + previousChunks: accumulatedChunks, + parser: resolvedFormat?.handler(request.output?.schema).parseChunk, + } + ) + ); return await generateHelper( registry, nextRequest, middleware, - currentTurn + 1 + currentTurn + 1, + messageIndex + 1 ); } diff --git a/js/ai/src/generate/chunk.ts b/js/ai/src/generate/chunk.ts index 4cf20d67a..231cbf939 100644 --- a/js/ai/src/generate/chunk.ts +++ b/js/ai/src/generate/chunk.ts @@ -31,9 +31,9 @@ export class GenerateResponseChunk implements GenerateResponseChunkData { /** The index of the message this chunk corresponds to, starting with `0` for the first model response of the generation. */ - index?: number; + index: number; /** The role of the message this chunk corresponds to. Will always be `model` or `tool`. */ - role?: Role; + role: Role; /** The content generated in this chunk. */ content: Part[]; /** Custom model-specific data for this chunk. */ @@ -45,21 +45,21 @@ export class GenerateResponseChunk constructor( data: GenerateResponseChunkData, - options?: { + options: { previousChunks?: GenerateResponseChunkData[]; - role?: Role; - index?: number; + role: Role; + index: number; parser?: ChunkParser; } ) { this.content = data.content || []; this.custom = data.custom; - this.previousChunks = options?.previousChunks + this.previousChunks = options.previousChunks ? [...options.previousChunks] : undefined; - this.index = options?.index; - this.role = options?.role; - this.parser = options?.parser; + this.index = options.index; + this.role = options.role; + this.parser = options.parser; } /** @@ -130,6 +130,14 @@ export class GenerateResponseChunk } toJSON(): GenerateResponseChunkData { - return { content: this.content, custom: this.custom }; + const data = { + role: this.role, + index: this.index, + content: this.content, + } as GenerateResponseChunkData; + if (this.custom) { + data.custom = this.custom; + } + return data; } } diff --git a/js/ai/src/model.ts b/js/ai/src/model.ts index 71a067b46..56bb3fb09 100644 --- a/js/ai/src/model.ts +++ b/js/ai/src/model.ts @@ -400,6 +400,9 @@ export type GenerateResponseData = z.infer; /** ModelResponseChunkSchema represents a chunk of content to stream to the client. */ export const ModelResponseChunkSchema = z.object({ + role: RoleSchema.optional(), + /** index of the message this chunk belongs to. */ + index: z.number().optional(), /** The chunk of content to stream right now. */ content: z.array(PartSchema), /** Model-specific extra information attached to this chunk. */ @@ -409,10 +412,7 @@ export const ModelResponseChunkSchema = z.object({ }); export type ModelResponseChunkData = z.infer; -export const GenerateResponseChunkSchema = ModelResponseChunkSchema.extend({ - /** @deprecated The index of the candidate this chunk belongs to. Always 0. */ - index: z.number().optional(), -}); +export const GenerateResponseChunkSchema = ModelResponseChunkSchema; export type GenerateResponseChunkData = z.infer< typeof GenerateResponseChunkSchema >; diff --git a/js/ai/tests/formats/array_test.ts b/js/ai/tests/formats/array_test.ts index a56752950..e85729b31 100644 --- a/js/ai/tests/formats/array_test.ts +++ b/js/ai/tests/formats/array_test.ts @@ -71,11 +71,17 @@ describe('arrayFormat', () => { for (const chunk of st.chunks) { const newChunk: GenerateResponseChunkData = { + index: 0, + role: 'model', content: [{ text: chunk.text }], }; const result = parser.parseChunk!( - new GenerateResponseChunk(newChunk, { previousChunks: chunks }) + new GenerateResponseChunk(newChunk, { + index: 0, + role: 'model', + previousChunks: chunks, + }) ); chunks.push(newChunk); diff --git a/js/ai/tests/formats/json_test.ts b/js/ai/tests/formats/json_test.ts index dff531b73..6a8e94420 100644 --- a/js/ai/tests/formats/json_test.ts +++ b/js/ai/tests/formats/json_test.ts @@ -67,11 +67,17 @@ describe('jsonFormat', () => { for (const chunk of st.chunks) { const newChunk: GenerateResponseChunkData = { + index: 0, + role: 'model', content: [{ text: chunk.text }], }; const result = parser.parseChunk!( - new GenerateResponseChunk(newChunk, { previousChunks: [...chunks] }) + new GenerateResponseChunk(newChunk, { + index: 0, + role: 'model', + previousChunks: [...chunks], + }) ); chunks.push(newChunk); diff --git a/js/ai/tests/formats/jsonl_test.ts b/js/ai/tests/formats/jsonl_test.ts index 4a58122fb..a5f3bce77 100644 --- a/js/ai/tests/formats/jsonl_test.ts +++ b/js/ai/tests/formats/jsonl_test.ts @@ -80,11 +80,17 @@ describe('jsonlFormat', () => { for (const chunk of st.chunks) { const newChunk: GenerateResponseChunkData = { + index: 0, + role: 'model', content: [{ text: chunk.text }], }; const result = parser.parseChunk!( - new GenerateResponseChunk(newChunk, { previousChunks: chunks }) + new GenerateResponseChunk(newChunk, { + index: 0, + role: 'model', + previousChunks: chunks, + }) ); chunks.push(newChunk); diff --git a/js/ai/tests/formats/text_test.ts b/js/ai/tests/formats/text_test.ts index 5dabc35f4..2c9fecd5f 100644 --- a/js/ai/tests/formats/text_test.ts +++ b/js/ai/tests/formats/text_test.ts @@ -54,11 +54,17 @@ describe('textFormat', () => { for (const chunk of st.chunks) { const newChunk: GenerateResponseChunkData = { + index: 0, + role: 'model', content: [{ text: chunk.text }], }; const result = parser.parseChunk!( - new GenerateResponseChunk(newChunk, { previousChunks: chunks }) + new GenerateResponseChunk(newChunk, { + index: 0, + role: 'model', + previousChunks: chunks, + }) ); chunks.push(newChunk); diff --git a/js/ai/tests/generate/chunk_test.ts b/js/ai/tests/generate/chunk_test.ts index febc670a1..21c5d6718 100644 --- a/js/ai/tests/generate/chunk_test.ts +++ b/js/ai/tests/generate/chunk_test.ts @@ -21,11 +21,11 @@ import { GenerateResponseChunk } from '../../src/generate.js'; describe('GenerateResponseChunk', () => { describe('text accumulation', () => { const testChunk = new GenerateResponseChunk( - { content: [{ text: 'new' }] }, + { index: 0, role: 'model', content: [{ text: 'new' }] }, { previousChunks: [ - { content: [{ text: 'old1' }] }, - { content: [{ text: 'old2' }] }, + { index: 0, role: 'model', content: [{ text: 'old1' }] }, + { index: 0, role: 'model', content: [{ text: 'old2' }] }, ], } ); diff --git a/js/ai/tests/generate/generate_test.ts b/js/ai/tests/generate/generate_test.ts index c7ff5ec10..594375144 100644 --- a/js/ai/tests/generate/generate_test.ts +++ b/js/ai/tests/generate/generate_test.ts @@ -369,7 +369,7 @@ describe('generate', () => { }); describe('generateStream', () => { - it('should pass a smoke test', async () => { + it('should stream out chunks', async () => { let registry = new Registry(); defineModel( @@ -390,11 +390,22 @@ describe('generate', () => { prompt: 'Testing streaming', }); - let streamed: string[] = []; + let streamed: any[] = []; for await (const chunk of stream) { - streamed.push(chunk.text); + streamed.push(chunk.toJSON()); } - assert.deepEqual(streamed, ['hello, ', 'world!']); + assert.deepStrictEqual(streamed, [ + { + index: 0, + role: 'model', + content: [{ text: 'hello, ' }], + }, + { + index: 0, + role: 'model', + content: [{ text: 'world!' }], + }, + ]); assert.deepEqual( (await response).messages.map((m) => m.content[0].text), ['Testing streaming', 'Testing streaming'] diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index 3120b458e..75a954898 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -401,19 +401,105 @@ describe('generate', () => { }, }; }; - const { text, output } = await ai.generate({ output: { schema }, prompt: 'call the tool', tools: ['testTool'], }); - assert.strictEqual(text, "```\n{foo: 'fromModel'}\n```"); assert.deepStrictEqual(output, { foo: 'fromModel', }); }); + it('streams the tool responses', async () => { + ai.defineTool( + { name: 'testTool', description: 'description' }, + async () => 'tool called' + ); + + // first response be tools call, the subsequent just text response from agent b. + let reqCounter = 0; + pm.handleResponse = async (req, sc) => { + if (sc) { + sc({ + content: [ + reqCounter === 0 + ? { + toolRequest: { + name: 'testTool', + input: {}, + ref: 'ref123', + }, + } + : { text: 'done' }, + ], + }); + } + return { + message: { + role: 'model', + content: [ + reqCounter++ === 0 + ? { + toolRequest: { + name: 'testTool', + input: {}, + ref: 'ref123', + }, + } + : { text: 'done' }, + ], + }, + }; + }; + + const { stream, response } = await ai.generateStream({ + prompt: 'call the tool', + tools: ['testTool'], + }); + + const chunks: any[] = []; + for await (const chunk of stream) { + chunks.push(chunk.toJSON()); + } + + assert.strictEqual((await response).text, 'done'); + assert.deepStrictEqual(chunks, [ + { + content: [ + { + toolRequest: { + input: {}, + name: 'testTool', + ref: 'ref123', + }, + }, + ], + index: 0, + role: 'model', + }, + { + content: [ + { + toolResponse: { + name: 'testTool', + output: 'tool called', + ref: 'ref123', + }, + }, + ], + index: 1, + role: 'model', + }, + { + content: [{ text: 'done' }], + index: 2, + role: 'model', + }, + ]); + }); + it('throws when exceeding max tool call iterations', async () => { ai.defineTool( { name: 'testTool', description: 'description' }, diff --git a/js/plugins/express/tests/express_test.ts b/js/plugins/express/tests/express_test.ts index 4866ef3f6..77d9bc85b 100644 --- a/js/plugins/express/tests/express_test.ts +++ b/js/plugins/express/tests/express_test.ts @@ -288,9 +288,9 @@ describe('expressHandler', async () => { } assert.deepStrictEqual(gotChunks, [ - { content: [{ text: '3' }] }, - { content: [{ text: '2' }] }, - { content: [{ text: '1' }] }, + { index: 0, role: 'model', content: [{ text: '3' }] }, + { index: 0, role: 'model', content: [{ text: '2' }] }, + { index: 0, role: 'model', content: [{ text: '1' }] }, ]); assert.strictEqual(await result.output(), 'Echo: olleh'); @@ -507,9 +507,9 @@ describe('startFlowServer', async () => { } assert.deepStrictEqual(gotChunks, [ - { content: [{ text: '3' }] }, - { content: [{ text: '2' }] }, - { content: [{ text: '1' }] }, + { index: 0, role: 'model', content: [{ text: '3' }] }, + { index: 0, role: 'model', content: [{ text: '2' }] }, + { index: 0, role: 'model', content: [{ text: '1' }] }, ]); assert.strictEqual(await result.output(), 'Echo: olleh'); diff --git a/js/testapps/flow-simple-ai/src/index.ts b/js/testapps/flow-simple-ai/src/index.ts index 419c18624..e973c0eca 100644 --- a/js/testapps/flow-simple-ai/src/index.ts +++ b/js/testapps/flow-simple-ai/src/index.ts @@ -572,11 +572,12 @@ ai.defineFlow( inputSchema: z.string(), outputSchema: z.string(), }, - async (query) => { + async (query, { sendChunk }) => { const { text } = await ai.generate({ model: gemini15Flash, prompt: query, tools: ['math/add', 'math/subtract'], + onChunk: sendChunk, }); return text; } From 55d08019344d3c50201ccd89f84d58bd37bd955b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:45:10 +0000 Subject: [PATCH 185/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.3 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 7efe9e808..6f7724e40 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 9ff7bcb14fedbfa9ae88b3e50db529e5ce0c0856 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:45:12 +0000 Subject: [PATCH 186/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.3 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 55571ffd8..5e7837b93 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From f3f6b59e3d79f9750437bb2b7a81bfde4e809f69 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:45:15 +0000 Subject: [PATCH 187/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.3 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index e95432d8d..a8dc3b4e9 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From 9399c1b4dcedfcf1f7d409ffc83822d252d8e297 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:46:21 +0000 Subject: [PATCH 188/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.3 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 33fc15b99..883066cfa 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From 1be2da2a0cfcbd7e9509dff1e447e3d749aee331 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:46:23 +0000 Subject: [PATCH 189/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.3 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index ec2df3685..bb413c94c 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From 78f5e07da71bad83c816a20172f053b3b2342954 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:46:26 +0000 Subject: [PATCH 190/562] chore: bump genkit version to genkit@1.0.0-rc.3 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index d537ad3f7..965850584 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From 05714e0d142b9f622a9eee072eb6545584970c2d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:46:29 +0000 Subject: [PATCH 191/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.3 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index 62e5e9e5e..e7ff64f76 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From 8e953e4fa51ef936dc96a7a43352e21be8a80b33 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:46:34 +0000 Subject: [PATCH 192/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.3 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 82d218d7f..2ee53980c 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From d65a865c26995d9c875f6541d1701638c9c3d7f7 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:46:37 +0000 Subject: [PATCH 193/562] chore: bump @genkit-ai/dotprompt version to @genkit-ai/dotprompt@1.0.0-rc.3 --- js/plugins/dotprompt/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dotprompt/package.json b/js/plugins/dotprompt/package.json index 9bb65b9c5..dc7356d2d 100644 --- a/js/plugins/dotprompt/package.json +++ b/js/plugins/dotprompt/package.json @@ -9,7 +9,7 @@ "prompting", "templating" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From 84f418bd49af9b34c04bdbdd846b0a29b288d60e Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:46:42 +0000 Subject: [PATCH 194/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.3 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index d3a54deeb..fff8c025e 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From 55a190a5c5d1d3855b211e060e97ce2fe74bf140 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:46:45 +0000 Subject: [PATCH 195/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.3 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 3a85cd3f1..f0974b423 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From 64a3bd965bbd1b78f1be7ca88c132c509c8d13e1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:46:47 +0000 Subject: [PATCH 196/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.3 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index ee1368e74..292eec051 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From e34ca5185af72494638d133a304893e0b7685857 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:46:50 +0000 Subject: [PATCH 197/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.3 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index aa0704755..765868fc8 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From bde0d2a7e2ce8785b775bc55dbf0ff4ee008c282 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:46:55 +0000 Subject: [PATCH 198/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.3 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index 4056dae9e..4c2abdeec 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From 89ceb91bddea96308ab64ae24c53a5cfcf54085d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:46:58 +0000 Subject: [PATCH 199/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.3 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index 97547108b..b3fd1cecb 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From 073f70227165e5880b495da0ca4a0ac756dfb61b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:47:01 +0000 Subject: [PATCH 200/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.3 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index 960053557..bb1826bf4 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From 69a8dca6709c1ec3e224f895439340b74e90138b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:47:04 +0000 Subject: [PATCH 201/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.3 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 051caa953..cb48d911e 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From a82bc34aa03fddf9a631f8e0ee6143c5dd20c866 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:47:06 +0000 Subject: [PATCH 202/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.3 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index ea4dfa095..6aa105790 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From b85344129ecea85cbc3f73271f7784130fe33e81 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:47:09 +0000 Subject: [PATCH 203/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.3 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index 73f45cf44..c4cc6c1ff 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From f0ade7e33c8ca81b9d43acf6c61b5c45dde20da7 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 16 Jan 2025 21:47:12 +0000 Subject: [PATCH 204/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.3 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index aa98043b0..d031b20c4 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.2", + "version": "1.0.0-rc.3", "type": "commonjs", "scripts": { "check": "tsc", From 3c313065f92f7ae9177ab2561b0372ed83aef14c Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Thu, 16 Jan 2025 17:42:18 -0500 Subject: [PATCH 205/562] docs(evals): Updated docs for evals (#1512) --- docs/evaluation.md | 534 +++++++++++++++++++++-------- docs/plugin-authoring-evaluator.md | 303 ++++++++-------- 2 files changed, 529 insertions(+), 308 deletions(-) diff --git a/docs/evaluation.md b/docs/evaluation.md index 5102ec901..efac99152 100644 --- a/docs/evaluation.md +++ b/docs/evaluation.md @@ -1,7 +1,7 @@ # Evaluation -Evaluations are a form of testing that helps you validate your LLM's responses -and ensure they meet your quality bar. +Evaluation is a form of testing that helps you validate your LLM's responses and +ensure they meet your quality bar. Firebase Genkit supports third-party evaluation tools through plugins, paired with powerful observability features that provide insight into the runtime state @@ -10,170 +10,421 @@ data including inputs, outputs, and information from intermediate steps to evaluate the end-to-end quality of LLM responses as well as understand the performance of your system's building blocks. -For example, if you have a RAG flow, Genkit will extract the set of documents -that was returned by the retriever so that you can evaluate the quality of your -retriever while it runs in the context of the flow as shown below with the -Genkit faithfulness and answer relevancy metrics: +### Types of evaluation -```ts -import { genkit } from 'genkit'; -import { genkitEval, GenkitMetric } from '@genkit-ai/evaluator'; -import { vertexAI, textEmbedding004, gemini15Flash } from '@genkit-ai/vertexai'; +Genkit supports two types of evaluation: -const ai = genkit({ - plugins: [ - vertexAI(), - genkitEval({ - judge: gemini15Flash, - metrics: [GenkitMetric.FAITHFULNESS, GenkitMetric.ANSWER_RELEVANCY], - embedder: textEmbedding004, // GenkitMetric.ANSWER_RELEVANCY requires an embedder - }), - ], - // ... -}); -``` +* **Inference-based evaluation**: This type of evaluation runs against a +collection of pre-determined inputs, assessing the corresponding outputs for +quality. -**Note:** The configuration above requires installing the `genkit`, -`@genkit-ai/googleai`, `@genkit-ai/evaluator` and `@genkit-ai/vertexai` -packages. + This is the most common evaluation type, suitable for most use cases. This + approach tests a system's actual output for each evaluation run. -```posix-terminal - npm install @genkit-ai/evaluator @genkit-ai/vertexai -``` + You can perform the quality assessment manually, by visually inspecting the + results. Alternatively, you can automate the assessment by using an + evaluation metric. -Start by defining a set of inputs that you want to use as an input dataset -called `testInputs.json`. This input dataset represents the test cases you will -use to generate output for evaluation. +* **Raw evaluation**: This type of evaluation directly assesses the quality of +inputs without any inference. This approach typically is used with automated +evaluation using metrics. All required fields for evaluation (e.g., `input`, +`context`, `output` and `reference`) must be present in the input dataset. This +is useful when you have data coming from an external source (e.g., collected +from your production traces) and you want to have an objective measurement of +the quality of the collected data. -```json -[ + For more information, see the [Advanced use](#advanced_use) section of this + page. + +This section explains how to perform inference-based evaluation using Genkit. + +## Quick start + +### Setup +

    +
  1. Use an existing Genkit app or create a new one by following our [Getting +started](get-started) guide.
  2. +
  3. Add the following code to define a simple RAG application to evaluate. For +this guide, we use a dummy retriever that always returns the same documents. + +```js +import { genkit, z, Document } from "genkit"; +import { + googleAI, + gemini15Flash, + gemini15Pro, +} from "@genkit-ai/googleai"; + +// Initialize Genkit +export const ai = genkit ({ + plugins: [ + googleAI(), + ] +}); + +// Dummy retriever that always returns the same docs +export const dummyRetriever = ai.defineRetriever( { - "input": "What is the French word for Cheese?" + name: "dummyRetriever", }, - { - "input": "What green vegetable looks like cauliflower?" + async (i) => { + const facts = [ + "Dog is man's best friend", + "Dogs have evolved and were domesticated from wolves", + ]; + // Just return facts as documents. + return { documents: facts.map((t) => Document.fromText(t)) }; } -] -``` - -If the evaluator requires a reference output for evaluating a flow, you can pass both -input and reference output using this format instead: +); -```json -[ - { - "input": "What is the French word for Cheese?", - "reference": "Fromage" +// A simple question-answering flow +export const qaFlow = ai.defineFlow({ + name: 'qaFlow', + inputSchema: z.string(), + outputSchema: z.string(), }, - { - "input": "What green vegetable looks like cauliflower?", - "reference": "Broccoli" + async (query) => { + const factDocs = await ai.retrieve({ + retriever: dummyRetriever, + query, + options: { k: 2 }, + }); + + const llmResponse = await ai.generate({ + model: gemini15Flash, + prompt: `Answer this question with the given context ${query}`, + docs: factDocs, + }); + return llmResponse.text; } -] +); ``` +
  4. +
  5. (Optional) Add evaluation metrics to your application to use while +evaluating. This guide uses the `MALICIOUSNESS` metric from the +`genkitEval` plugin. -Note that you can use any JSON data type in the input JSON file. Genkit will pass them along with the same data type to your flow. - -You can then use the `eval:flow` command to evaluate your flow against the test -cases provided in `testInputs.json`. +```js +import { genkitEval, GenkitMetric } from "@genkit-ai/evaluator"; +import { gemini15Pro } from "@genkit-ai/googleai"; -```posix-terminal -genkit eval:flow menuSuggestionFlow --input testInputs.json +export const ai = genkit ({ + plugins: [ + ... + // Add this plugin to your Genkit initialization block + genkitEval({ + judge: gemini15Pro, + metrics: [GenkitMetric.MALICIOUSNESS], + }), + ] +}); ``` -If your flow requires auth, you may specify it using the `--auth` argument: +**Note:** The configuration above requires installing the +[`@genkit-ai/evaluator`](https://www.npmjs.com/package/@genkit-ai/evaluator) +package. ```posix-terminal -genkit eval:flow menuSuggestionFlow --input testInputs.json --auth "{\"email_verified\": true}" + npm install @genkit-ai/evaluator ``` - -You can then see evaluation results in the Developer UI by running: +
  6. +
  7. Start your Genkit application ```posix-terminal -genkit start +genkit start -- ``` +
  8. +
-Then navigate to `localhost:4000/evaluate`. +### Create a dataset -Alternatively, you can provide an output file to inspect the output in a JSON -file. +Create a dataset to define the examples we want to use for evaluating our flow. -```posix-terminal -genkit eval:flow menuSuggestionFlow --input testInputs.json --output eval-result.json -``` +1. Go to the Dev UI at `http://localhost:4000` and click the **Datasets** button +to open the Datasets page. + +2. Click on the **Create Dataset** button to open the create dataset dialog. + + a. Provide a `datasetId` for your new dataset. This guide uses + `myFactsQaDataset`. + + b. Select `Flow` dataset type. + + c. Leave the validation target field empty and click **Save** + +3. Your new dataset page appears, showing an empty dataset. Add examples to it + by following these steps: + + a. Click the **Add example** button to open the example editor panel. + + b. Only the `input` field is required. Enter `"Who is man's best friend?"` + in the `input` field, and click **Save** to add the example has to your + dataset. + + c. Repeat steps (a) and (b) a couple more times to add more examples. This + guide adds the following example inputs to the dataset: + + ``` + "Can I give milk to my cats?" + "From which animals did dogs evolve?" + ``` + + By the end of this step, your dataset should have 3 examples in it, with the + values mentioned above. + +### Run evaluation and view results + +To start evaluating the flow, click the `Evaluations` tab in the Dev UI and +click the **Run new evaluation** button to get started. + +1. Select the `Flow` radio button to evaluate a flow. + +2. Select `qaFlow` as the target flow to evaluate. + +3. Select `myFactsQaDataset` as the target dataset to use for evaluation. + +4. (Optional) If you have installed an evaluator metric using Genkit plugins, +you can see these metrics in this page. Select the metrics that you want to use +with this evaluation run. This is entirely optional: Omitting this step will +still return the results in the evaluation run, but without any associated +metrics. + +5. Finally, click **Run evaluation** to start evaluation. Depending on the flow +you're testing, this may take a while. Once the evaluation is complete, a +success message appears with a link to view the results. Click on the link to go +to the _Evaluation details_ page. + +You can see the details of your evaluation on this page, including original +input, extracted context and metrics (if any). + +## Core concepts + +### Terminology + +- **Evaluation**: An evaluation is a process that assesses system performance. +In Genkit, such a system is usually a Genkit primitive, such as a flow or a +model. An evaluation can be automated or manual (human evaluation). + +- **Bulk inference** Inference is the act of running an input on a flow or model +to get the corresponding output. Bulk inference involves performing inference on +multiple inputs simultaneously. + +- **Metric** An evaluation metric is a criterion on which an inference is +scored. Examples include accuracy, faithfulness, maliciousness, whether the +output is in English, etc. + +- **Dataset** A dataset is a collection of examples to use for inference-based +evaluation. A dataset typically consists of `input` and optional `reference` +fields. The `reference` field does not affect the inference step of evaluation +but it is passed verbatim to any evaluation metrics. In Genkit, you can create a +dataset through the Dev UI. There are two types of datasets in Genkit: _Flow_ +datasets and _Model_ datasets. + +### Schema validation + +Depending on the type, datasets have schema validation support in the Dev UI: -**Note:** Below you can see an example of how an LLM can help you generate the -test cases. +* Flow datasets support validation of the `input` and `reference` fields of the +dataset against a flow in the Genkit application. Schema validation is optional +and is only enforced if a schema is specified on the target flow. + +* Model datasets have implicit schema, supporting both `string` and + `GenerateRequest` input types. String validation provides a convenient way to + evaluate simple text prompts, while `GenerateRequest` provides complete + control for advanced use cases (e.g. providing model parameters, message + history, tools, etc). You can find the full schema for `GenerateRequest` in + our [API reference + docs](https://genkit-js-api.web.app/interfaces/genkit._.GenerateRequest.html). + + +Note: Schema validation is a helper tool for editing examples, but it is +possible to save an example with invalid schema. These examples may fail when +the running an evaluation. ## Supported evaluators ### Genkit evaluators -Genkit includes a small number of native evaluators, inspired by RAGAS, to help -you get started: +Genkit includes a small number of native evaluators, inspired by +[RAGAS](https://docs.ragas.io/en/stable/), to help you get started: -* Faithfulness -* Answer Relevancy -* Maliciousness +* Faithfulness -- Measures the factual consistency of the generated answer +against the given context +* Answer Relevancy -- Assesses how pertinent the generated answer is to the +given prompt +* Maliciousness -- Measures whether the generated output intends to deceive, +harm, or exploit ### Evaluator plugins -Genkit supports additional evaluators through plugins like the VertexAI Rapid Evaluators via the [VertexAI Plugin](./plugins/vertex-ai#evaluators). +Genkit supports additional evaluators through plugins, like the Vertex Rapid +Evaluators, which you access via the [VertexAI +Plugin](./plugins/vertex-ai#evaluators). ## Advanced use -`eval:flow` is a convenient way to quickly evaluate the flow, but sometimes you -might need more control over evaluation steps. This may occur if you are using a -different framework and already have some output you would like to evaluate. You -can perform all the steps that `eval:flow` performs semi-manually. +### Evaluation using the CLI -You can batch run your Genkit flow and add a unique label to the run which then -will be used to extract an evaluation dataset (a set of inputs, outputs, and -contexts). +Genkit CLI provides a rich API for performing evaluation. This is especially +useful in environments where the Dev UI is not available (e.g. in a CI/CD +workflow). + +Genkit CLI provides 3 main evaluation commands: `eval:flow`, `eval:extractData`, +and `eval:run`. + +#### `eval:flow` command -Run the flow over your test inputs: +The `eval:flow` command runs inference-based evaluation on an input dataset. +This dataset may be provided either as a JSON file or by referencing an existing +dataset in your Genkit runtime. ```posix-terminal -genkit flow:batchRun myRagFlow test_inputs.json --output flow_outputs.json --label customLabel -``` +# Referencing an existing dataset +genkit eval:flow qaFlow --input myFactsQaDataset -Extract the evaluation data: +# or, using a dataset from a file +genkit eval:flow qaFlow --input testInputs.json +``` +Note: Make sure that you start your genkit app before running these CLI +commands. ```posix-terminal -genkit eval:extractData myRagFlow --label customLabel --output customLabel_dataset.json +genkit start -- ``` -The exported data will be output as a JSON file with each testCase in the -following format: +Here, `testInputs.json` should be an array of objects containing an `input` +field and an optional `reference` field, like below: ```json [ { - "testCaseId": string, - "input": string, - "output": string, - "context": array of strings, - "traceIds": array of strings, + "input": "What is the French word for Cheese?", + }, + { + "input": "What green vegetable looks like cauliflower?", + "reference": "Broccoli" } ] ``` -The data extractor will automatically locate retrievers and add the produced -docs to the context array. By default, `eval:run` will run against all -configured evaluators, and like `eval:flow`, results for `eval:run` will appear -in the evaluation page of Developer UI, located at `localhost:4000/evaluate`. +If your flow requires auth, you may specify it using the `--auth` argument: + +```posix-terminal +genkit eval:flow qaFlow --input testInputs.json --auth "{\"email_verified\": true}" +``` + +By default, the `eval:flow` and `eval:run` commands use all available metrics +for evaluation. To run on a subset of the configured evaluators, use the +`--evaluators` flag and provide a comma-separated list of evaluators by name: + +```posix-terminal +genkit eval:flow qaFlow --input testInputs.json --evaluators=genkit/faithfulness,genkit/answer_relevancy +``` +You can view the results of your evaluation run in the Dev UI at +`localhost:4000/evaluate`. + +#### `eval:extractData` and `eval:run` commands + +To support *raw evaluation*, Genkit provides tools to extract data from traces +and run evaluation metrics on extracted data. This is useful, for example, if +you are using a different framework for evaluation or if you are collecting +inferences from a different environment to test locally for output quality. + +You can batch run your Genkit flow and add a unique label to the run which then +can be used to extract an *evaluation dataset*. A raw evaluation dataset is a +collection of inputs for evaluation metrics, *without* running any prior +inference. + +Run your flow over your test inputs: + +```posix-terminal +genkit flow:batchRun qaFlow testInputs.json --label firstRunSimple +``` + +Extract the evaluation data: + +```posix-terminal +genkit eval:extractData qaFlow --label firstRunSimple --output factsEvalDataset.json +``` + +The exported data has a format different from the dataset format presented +earlier. This is because this data is intended to be used with evaluation +metrics directly, without any inference step. Here is the syntax of the +extracted data. + +```json +Array<{ + "testCaseId": string, + "input": any, + "output": any, + "context": any[], + "traceIds": string[], +}>; +``` + +The data extractor automatically locates retrievers and adds the produced docs +to the context array. You can run evaluation metrics on this extracted dataset +using the `eval:run` command. + +```posix-terminal +genkit eval:run factsEvalDataset.json +``` + +By default, `eval:run` runs against all configured evaluators, and as with +`eval:flow`, results for `eval:run` appear in the evaluation page of Developer +UI, located at `localhost:4000/evaluate`. ### Custom extractors -You can also provide custom extractors to be used in `eval:extractData` and -`eval:flow` commands. Custom extractors allow you to override the default -extraction logic giving you more power in creating datasets and evaluating them. +Genkit provides reasonable default logic for extracting the necessary fields +(`input`, `output` and `context`) while doing an evaluation. However, you may +find that you need more control over the extraction logic for these fields. +Genkit supports customs extractors to achieve this. You can provide custom +extractors to be used in `eval:extractData` and `eval:flow` commands. -To configure custom extractors, add a tools config file named -`genkit-tools.conf.js` to your project root if you don't have one already. +First, as a preparatory step, introduce an auxilary step in our `qaFlow` +example: + +```js +export const qaFlow = ai.defineFlow({ + name: 'qaFlow', + inputSchema: z.string(), + outputSchema: z.string(), + }, + async (query) => { + const factDocs = await ai.retrieve({ + retriever: dummyRetriever, + query, + options: { k: 2 }, + }); + const factDocsModified = await run('factModified', async () => { + // Let us use only facts that are considered silly. This is a + // hypothetical step for demo purposes, you may perform any + // arbitrary task inside a step and reference it in custom + // extractors. + // + // Assume you have a method that checks if a fact is silly + return factDocs.filter(d => isSillyFact(d.text)); + }); + + const llmResponse = await ai.generate({ + model: gemini15Flash, + prompt: `Answer this question with the given context ${query}`, + docs: factDocs, + }); + return llmResponse.text; + } +); +``` + +Next, configure a custom extractor to use the output of the `factModified` step +when evaluating this flow. + +If you don't have one a tools-config file to configure custom extractors, add +one named `genkit-tools.conf.js` to your project root. ```posix-terminal -cd $GENKIT_PROJECT_HOME +cd /path/to/your/genkit/app touch genkit-tools.conf.js ``` @@ -184,21 +435,26 @@ In the tools config file, add the following code: module.exports = { evaluators: [ { - actionRef: '/flow/myFlow', + actionRef: '/flow/qaFlow', extractors: { - context: { outputOf: 'foo-step' }, - output: 'bar-step', + context: { outputOf: 'factModified' }, }, }, ], }; ``` -In this sample, you configure an extractor for `myFlow` flow. The config -overrides the extractors for `context` and `output` fields and uses the default -logic for the `input` field. +This config overrides the default extractors of Genkit's tooling, specifically +changing what is considered as `context` when evaluating this flow. + +Running evaluation again reveals that context is now populated as the output of +the step `factModified`. -The specification of the evaluation extractors is as follows: +```posix-terminal +genkit eval:flow qaFlow --input testInputs.json +``` + +Evaluation extractors are specified as follows: * `evaluators` field accepts an array of EvaluatorConfig objects, which are scoped by `flowName` @@ -212,59 +468,46 @@ The specification of the evaluation extractors is as follows: inputOf: 'foo-step' }` would extract the input of step `foo-step` for this key. * `(trace) => string;` - For further flexibility, you can provide a - function that accepts a Genkit trace and returns a `string`, and specify - the extraction logic inside this function. Refer to + function that accepts a Genkit trace and returns an `any`-type value, + and specify the extraction logic inside this function. Refer to `genkit/genkit-tools/common/src/types/trace.ts` for the exact TraceData schema. -**Note:** The extracted data for all these steps will be a JSON string. The -tooling will parse this JSON string at the time of evaluation automatically. If -providing a function extractor, make sure that the output is a valid JSON -string. For example: `"Hello, world!"` is not valid JSON; `"\"Hello, world!\""` -is valid. - -### Running on existing datasets - -To run evaluation over an already extracted dataset: - -```posix-terminal -genkit eval:run customLabel_dataset.json -``` - -To output to a different location, use the `--output` flag. - -```posix-terminal -genkit eval:flow menuSuggestionFlow --input testInputs.json --output customLabel_evalresult.json -``` - -To run on a subset of the configured evaluators, use the `--evaluators` flag and -provide a comma-separated list of evaluators by name: - -```posix-terminal -genkit eval:run customLabel_dataset.json --evaluators=genkit/faithfulness,genkit/answer_relevancy -``` +**Note:** The extracted data for all these extractors is the type corresponding +to the extractor. For example, if you use context: `{ outputOf: 'foo-step' }`, +and `foo-step` returns an array of objects, the extracted context is also an +array of objects. ### Synthesizing test data using an LLM -Here's an example flow that uses a PDF file to generate possible questions users -might be asking about it. +Here is an example flow that uses a PDF file to generate potential user +questions. ```ts import { genkit, run, z } from "genkit"; import { googleAI, gemini15Flash } from "@genkit-ai/googleai"; -import { chunk } from "llm-chunk"; -import path from 'path'; +import { chunk } from "llm-chunk"; // npm i llm-chunk +import path from "path"; +import { readFile } from "fs/promises"; +import pdf from "pdf-parse"; // npm i pdf-parse const ai = genkit({ plugins: [googleAI()] }); const chunkingConfig = { minLength: 1000, // number of minimum characters into chunk maxLength: 2000, // number of maximum characters into chunk - splitter: 'sentence', // paragraph | sentence + splitter: "sentence", // paragraph | sentence overlap: 100, // number of overlap chracters - delimiters: '', // regex for base split method + delimiters: "", // regex for base split method } as any; +async function extractText(filePath: string) { + const pdfFile = path.resolve(filePath); + const dataBuffer = await readFile(pdfFile); + const data = await pdf(dataBuffer); + return data.text; +} + export const synthesizeQuestions = ai.defineFlow( { name: "synthesizeQuestions", @@ -274,7 +517,6 @@ export const synthesizeQuestions = ai.defineFlow( async (filePath) => { filePath = path.resolve(filePath); // `extractText` loads the PDF and extracts its contents as text. - // See our RAG documentation for more details. const pdfTxt = await run("extract-text", () => extractText(filePath)); const chunks = await run("chunk-it", async () => diff --git a/docs/plugin-authoring-evaluator.md b/docs/plugin-authoring-evaluator.md index d7ca8a7bd..732d855f8 100644 --- a/docs/plugin-authoring-evaluator.md +++ b/docs/plugin-authoring-evaluator.md @@ -1,16 +1,24 @@ # Writing a Genkit Evaluator -Firebase Genkit can be extended to support custom evaluation of test case output, either by using an LLM as a judge, or purely programmatically. +Firebase Genkit can be extended to support custom evaluation, using either an +LLM as a judge, or by programmatic (heuristic) evaluation. ## Evaluator definition -Evaluators are functions that assess the content given to and generated by an LLM. There are two main approaches to automated evaluation (testing): heuristic assessment and LLM-based assessment. In the heuristic approach, you define a deterministic function like those of traditional software development. In an LLM-based assessment, the content is fed back to an LLM and the LLM is asked to score the output according to criteria set in a prompt. +Evaluators are functions that assess an LLM's response. There are two main +approaches to automated evaluation: heuristic evaluation and LLM-based +evaluation. In the heuristic approach, you define a deterministic function, +whereas in an LLM-based assessment, the content is fed back to an LLM and the +LLM is asked to score the output according to criteria set in a prompt. -Regardless of the approach you take, you need to use the `ai.defineEvaluator` method to define an evaluator action in Genkit. We will see a couple of examples of how to use this method in this document. +Both approaches are supported by the `ai.defineEvaluator` method to define an +evaluator action in Genkit. This document explores a couple of examples on how +to use this method for heuristic and LLM-based evaluations. ### LLM based Evaluators -An LLM-based evaluator leverages an LLM to evaluate the input, context, or output of your generative AI feature. +An LLM-based evaluator leverages an LLM to evaluate the `input`, `context`, and +`output` of your generative AI feature. LLM-based evaluators in Genkit are made up of 3 components: @@ -20,49 +28,66 @@ LLM-based evaluators in Genkit are made up of 3 components: #### Define the prompt -For this example, the prompt is going to ask the LLM to judge how delicious the output is. First, provide context to the LLM, then describe what you want it to do, and finally, give it a few examples to base its response on. +For this example, the evaluator leverages an LLM to determine whether an +`output` is delicious or not. First, provide context to the LLM, then describe +what you want it to do, and finally, give it a few examples to base its response +on. -Genkit’s `definePrompt` utility provides an easy way to define prompts with input and output validation. Here’s how you can set up an evaluation prompt with `definePrompt`. +Genkit’s `definePrompt` utility provides an easy way to define prompts with +input and output validation. You can set up an evaluation prompt with +`definePrompt` as follows: ```ts +import { z } from "genkit"; + const DELICIOUSNESS_VALUES = ['yes', 'no', 'maybe'] as const; const DeliciousnessDetectionResponseSchema = z.object({ reason: z.string(), verdict: z.enum(DELICIOUSNESS_VALUES), }); -type DeliciousnessDetectionResponse = z.infer; -const DELICIOUSNESS_PROMPT = ai.definePrompt( - { - name: 'deliciousnessPrompt', - inputSchema: z.object({ - output: z.string(), - }), - outputSchema: DeliciousnessDetectionResponseSchema, - }, - `You are a food critic. Assess whether the provided output sounds delicious, giving only "yes" (delicious), "no" (not delicious), or "maybe" (undecided) as the verdict. +function getDeliciousnessPrompt(ai: Genkit) { + return ai.definePrompt({ + name: 'deliciousnessPrompt', + input: { + schema: z.object({ + responseToTest: z.string(), + }), + }, + output: { + schema: DeliciousnessDetectionResponseSchema, + } + }, + `You are a food critic. Assess whether the provided output sounds delicious, giving only "yes" (delicious), "no" (not delicious), or "maybe" (undecided) as the verdict. - Examples: - Output: Chicken parm sandwich - Response: { "reason": "A classic and beloved dish.", "verdict": "yes" } + Examples: + Output: Chicken parm sandwich + Response: { "reason": "A classic and beloved dish.", "verdict": "yes" } - Output: Boston Logan Airport tarmac - Response: { "reason": "Not edible.", "verdict": "no" } + Output: Boston Logan Airport tarmac + Response: { "reason": "Not edible.", "verdict": "no" } - Output: A juicy piece of gossip - Response: { "reason": "Metaphorically 'tasty' but not food.", "verdict": "maybe" } + Output: A juicy piece of gossip + Response: { "reason": "Metaphorically 'tasty' but not food.", "verdict": "maybe" } - New Output: - {{output}} - Response: - ` -); + New Output: + {% verbatim %} + {{ responseToTest }} + {% endverbatim %} + Response: + ` + ); +} ``` #### Define the scoring function -Now, define the function that will take an example which includes `output` as is required by the prompt and score the result. Genkit test cases include `input` as required a required field, with optional fields for `output` and `context`. It is the responsibility of the evaluator to validate that all fields required for evaluation are present. +Define a function that takes an example which includes `output` as it is +required by the prompt, and scores the result. Genkit testcases include `input` as +a required field, with `output` and `context` as optional fields. It is the +responsibility of the evaluator to validate that all fields required for +evaluation are present. ```ts import { ModelArgument, z } from 'genkit'; @@ -84,17 +109,17 @@ export async function deliciousnessScore< throw new Error('Output is required for Deliciousness detection'); } - //Hydrate the prompt - const finalPrompt = DELICIOUSNESS_PROMPT.renderText({ - output: d.output as string, - }); - - // Call the LLM to generate an evaluation result - const response = await ai.generate({ - model: judgeLlm, - prompt: finalPrompt, - config: judgeConfig, - }); + // Hydrate the prompt and generate an evaluation result + const deliciousnessPrompt = getDeliciousnessPrompt(ai); + const response = await deliciousnessPrompt( + { + responseToTest: d.output as string, + }, + { + model: judgeLlm, + config: judgeConfig, + } + ); // Parse the output const parsedResponse = response.output; @@ -112,10 +137,10 @@ export async function deliciousnessScore< #### Define the evaluator action -The final step is to write a function that defines the evaluator action itself. +The final step is to write a function that defines the `EvaluatorAction`. ```ts -import { Genkit, ModelReference, z } from 'genkit'; +import { Genkit, z } from 'genkit'; import { BaseEvalDataPoint, EvaluatorAction } from 'genkit/evaluator'; /** @@ -125,12 +150,12 @@ export function createDeliciousnessEvaluator< ModelCustomOptions extends z.ZodTypeAny, >( ai: Genkit, - judge: ModelReference, - judgeConfig: z.infer + judge: ModelArgument, + judgeConfig?: z.infer ): EvaluatorAction { return ai.defineEvaluator( { - name: `myAwesomeEval/deliciousness`, + name: `myCustomEvals/deliciousnessEvaluator`, displayName: 'Deliciousness', definition: 'Determines if output is considered delicous.', isBilled: true, @@ -146,7 +171,12 @@ export function createDeliciousnessEvaluator< } ``` -The `defineEvaluator` method is similar to other Genkit constructors like `defineFlow`, `defineRetriever` etc. The user should provide an `EvaluatorFn` to the `defineEvaluator` callback. The `EvaluatorFn` accepts a `BaseEvalDataPoint` which corresponds to a single entry in a dataset under evaluation, along with an optional custom options parameter if specified. The function, should process the datapoint and return an `EvalResponse` object. +The `defineEvaluator` method is similar to other Genkit constructors like +`defineFlow`, `defineRetriever`, etc. This method requires an `EvaluatorFn` to +be provided as a callback method. The `EvaluatorFn` accepts a +`BaseEvalDataPoint` which corresponds to a single entry in a dataset under +evaluation, along with an optional custom options parameter if specified. The +function processes the datapoint and returns an `EvalResponse` object. Here are the Zod Schemas for `BaseEvalDataPoint` and `EvalResponse`: @@ -185,11 +215,18 @@ const ScoreSchema = z.object({ }); ``` -`defineEvaluator` lets the user provide a name and user-readable display name and a definition for the evaluator. The display name and definiton will be displayed in evaluation runs in the Dev UI. It also has an optional `isBilled` option which marks whether this evaluator may result in billing (eg: if it uses a billed LLM or API). If an evaluator is billed, the user is prompted for a confirmation in the CLI before they can run an evaluation, to help guard from unintended expenses. +`defineEvaluator` lets the user provide a name, a user-readable display name, +and a definition for the evaluator. The display name and definiton are displayed +along with evaluation results in the Dev UI. It also has an optional `isBilled` +option which marks whether this evaluator may result in billing (e.g.: it uses +a billed LLM or API). If an evaluator is billed, the user is prompted for a +confirmation in the CLI before they can run an evaluation, to help guard from +unintended expenses. ### Heuristic Evaluators -A heuristic evaluator can be any function used to evaluate the input, context, or output of your generative AI feature. +A heuristic evaluator can be any function used to evaluate the `input`, `context`, +or `output` of your generative AI feature. Heuristic evaluators in Genkit are made up of 2 components: @@ -198,17 +235,18 @@ Heuristic evaluators in Genkit are made up of 2 components: #### Define the scoring function -Just like the LLM-based evaluator, define the scoring function. In this case, the scoring function does not need to know about the judge LLM or its config. +Similar to the LLM-based evaluator, define the scoring function. In this case, +the scoring function does not need a judge LLM. ```ts import { EvalResponses } from 'genkit'; import { BaseEvalDataPoint, Score } from 'genkit/evaluator'; const US_PHONE_REGEX = - /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4}$/i; + /[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4}/i; /** - * Scores whether an individual datapoint matches a US Phone Regex. + * Scores whether a datapoint output contains a US Phone number. */ export async function usPhoneRegexScore( dataPoint: BaseEvalDataPoint @@ -219,23 +257,13 @@ export async function usPhoneRegexScore( } const matches = US_PHONE_REGEX.test(d.output as string); const reasoning = matches - ? `Output matched regex ${US_PHONE_REGEX.source}` - : `Output did not match regex ${US_PHONE_REGEX.source}`; + ? `Output matched US_PHONE_REGEX` + : `Output did not match US_PHONE_REGEX`; return { score: matches, details: { reasoning }, }; } - -/** - * Create an EvalResponses from an individual scored datapoint. - */ -function fillScores(dataPoint: BaseEvalDataPoint, score: Score): EvalResponses { - return { - testCaseId: dataPoint.testCaseId, - evaluation: score, - }; -} ``` #### Define the evaluator action @@ -247,141 +275,89 @@ import { BaseEvalDataPoint, EvaluatorAction } from 'genkit/evaluator'; /** * Configures a regex evaluator to match a US phone number. */ -export function createUSPhoneRegexEvaluator( - ai: Genkit, - metric: MyAwesomeMetric -): EvaluatorAction { +export function createUSPhoneRegexEvaluator(ai: Genkit): EvaluatorAction { return ai.defineEvaluator( { - name: `myAwesomeEval/${metric.toLocaleLowerCase()}`, - displayName: 'Regex Match', - definition: - 'Runs the output against a regex and responds with true if a match is found and false otherwise.', + name: `myCustomEvals/usPhoneRegexEvaluator`, + displayName: "Regex Match for US PHONE NUMBER", + definition: "Uses Regex to check if output matches a US phone number", isBilled: false, }, async (datapoint: BaseEvalDataPoint) => { const score = await usPhoneRegexScore(datapoint); - return fillScores(datapoint, score); + return { + testCaseId: datapoint.testCaseId, + evaluation: score, + }; } ); } - ``` -## Configuration - -### Plugin Options - -Define the `PluginOptions` that the custom evaluator plugin will use. This object has no strict requirements and is dependent on the types of evaluators that are defined. - -At a minimum it will need to take the definition of which metrics to register. - -```ts -export enum MyAwesomeMetric { - WORD_COUNT = 'WORD_COUNT', - DELICIOUSNESS = 'DELICIOUSNESS', - US_PHONE_REGEX_MATCH = 'US_PHONE_REGEX_MATCH', -} - -export interface PluginOptions { - metrics?: Array; -} -``` - -If this new plugin uses an LLM as a judge and the plugin supports swapping out which LLM to use, define additional parameters in the `PluginOptions` object. - -```ts -export interface PluginOptions { - judge: ModelReference; - judgeConfig?: z.infer; - metrics?: Array; -} -``` +## Putting it together ### Plugin definition -Plugins are registered with the framework via the `genkit.config.ts` file in a project. To be able to configure a new plugin, define a function that defines a `GenkitPlugin` and configures it with the `PluginOptions` defined above. +Plugins are registered with the framework by installing them at the time of +initializing Genkit. To define a new plugin, use the `genkitPlugin` helper +method to instantiate all Genkit actions within the plugin context. -In this case we have two evaluators `DELICIOUSNESS` and `US_PHONE_REGEX_MATCH`. This is where those evaluators are registered with the plugin and with Firebase Genkit. +Here we have two evaluators,the LLM-based deliciousness evaluator and the +regex-based US phone number evaluator. Instatiating these evaluators within the +plugin context registeres them with the plugin. ```ts import { GenkitPlugin, genkitPlugin } from 'genkit/plugin'; -export function myAwesomeEval( - options: PluginOptions -): GenkitPlugin { +export function myCustomEvals< + ModelCustomOptions extends z.ZodTypeAny +>(options: { + judge: ModelArgument; + judgeConfig?: ModelCustomOptions; +}): GenkitPlugin { // Define the new plugin - return genkitPlugin( - 'myAwesomeEval', - async (ai: Genkit) => { - const { judge, judgeConfig, metrics } = options; - const evaluators: EvaluatorAction[] = metrics.map((metric) => { - switch (metric) { - case MyAwesomeMetric.DELICIOUSNESS: - // This evaluator requires an LLM as judge - return createDeliciousnessEvaluator(ai, judge, judgeConfig); - case MyAwesomeMetric.US_PHONE_REGEX_MATCH: - // This evaluator does not require an LLM - return createUSPhoneRegexEvaluator(ai, metric); - } - }); - } - ); + return genkitPlugin("myCustomEvals", async (ai: Genkit) => { + const { judge, judgeConfig } = options; + + // The plugin instatiates our custom evaluators within the context + // of the `ai` object, making them available + // throughout our Genkit application. + createDeliciousnessEvaluator(ai, judge, judgeConfig); + createUSPhoneRegexEvaluator(ai); + }); } -export default myAwesomeEval; +export default myCustomEvals; ``` ### Configure Genkit -Add the newly defined plugin to your Genkit configuration. +Add the `myCustomEvals` plugin to your Genkit configuration. -For evaluation with Gemini, disable safety settings so that the evaluator can accept, detect, and score potentially harmful content. +For evaluation with Gemini, disable safety settings so that the evaluator can +accept, detect, and score potentially harmful content. ```ts -import { gemini15Flash } from '@genkit-ai/googleai'; +import { gemini15Pro } from '@genkit-ai/googleai'; const ai = genkit({ plugins: [ + vertexAI(), ... - myAwesomeEval({ - judge: gemini15Flash, - judgeConfig: { - safetySettings: [ - { - category: 'HARM_CATEGORY_HATE_SPEECH', - threshold: 'BLOCK_NONE', - }, - { - category: 'HARM_CATEGORY_DANGEROUS_CONTENT', - threshold: 'BLOCK_NONE', - }, - { - category: 'HARM_CATEGORY_HARASSMENT', - threshold: 'BLOCK_NONE', - }, - { - category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', - threshold: 'BLOCK_NONE', - }, - ], - }, - metrics: [ - MyAwesomeMetric.DELICIOUSNESS, - MyAwesomeMetric.US_PHONE_REGEX_MATCH - ], + myCustomEvals({ + judge: gemini15Pro, }), ], ... }); ``` -## Testing +## Using your custom evaluators -The same issues that apply to evaluating the quality of the output of a generative AI feature apply to evaluating the judging capacity of an LLM-based evaluator. +Once you instantiate your custom evaluators within the Genkit app context (either +through a plugin or directly), they are ready to be used. Let us try out the +deliciousness evaluator with a few sample inputs and outputs. -To get a sense of whether the custom evaluator performs at the expected level, create a set of test cases that have a clear right and wrong answer. - -As an example for deliciousness, that might look like a json file `deliciousness_dataset.json`: +Create a json file `deliciousness_dataset.json` with the following content: ```json [ @@ -398,15 +374,18 @@ As an example for deliciousness, that might look like a json file `deliciousness ] ``` -These examples can be human generated or you can ask an LLM to help create a set of test cases that can be curated. There are many available benchmark datasets that can be used as well. - -Then use the Genkit CLI to run the evaluator against these test cases. +Use the Genkit CLI to run the evaluator against these test cases. ```posix-terminal # Start your genkit runtime genkit start -- -genkit eval:run deliciousness_dataset.json +genkit eval:run deliciousness_dataset.json --evaluators=myCustomEvals/deliciousnessEvaluator ``` Navigate to `localhost:4000/evaluate` to view your results in the Genkit UI. + +It is important to note that confidence in custom evaluators increases as +you benchmark them with standard datasets or approaches. Iterate on the results +of such benchmarks to improve your evaluators' performance till it reaches the +desired quality. From 38258220f93880fea89d57ab9e70576f878fe809 Mon Sep 17 00:00:00 2001 From: Max Lord Date: Fri, 17 Jan 2025 10:09:34 -0500 Subject: [PATCH 206/562] fix(js): Marking root spans that have failed (#1615) --- .../google-cloud/src/gcpOpenTelemetry.ts | 22 +++++++++++++++++-- js/plugins/google-cloud/tests/traces_test.ts | 21 ++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts index d543d66a8..cada54632 100644 --- a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts +++ b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts @@ -307,6 +307,26 @@ class AdjustingTraceExporter implements SpanExporter { ) ); + // Check if we have a failure in the root span that requires special handling + const rootSpan = spans.find((s) => + Object.keys(s.attributes).includes('genkit:isRoot') + ); + if (rootSpan) { + const rootSpanFailed = + (rootSpan.attributes['genkit:state'] as string) === 'error'; + const anotherFailedSpan = spans.find( + (s) => + !Object.keys(s.attributes).includes('genkit:isRoot') && + s.attributes['genkit:state'] === 'error' + ); + if (rootSpanFailed && !anotherFailedSpan) { + rootSpan.attributes['genkit:failedSpan'] = + rootSpan.attributes['genkit:name']; + rootSpan.attributes['genkit:failedPath'] = + rootSpan.attributes['genkit:path']; + } + } + return spans.map((span) => { this.tickTelemetry(span, allLeafPaths); @@ -389,8 +409,6 @@ class AdjustingTraceExporter implements SpanExporter { }; } - // This is a workaround for GCP Trace to mark a span with a red - // exclamation mark indicating that it is an error. private normalizeLabels(span: ReadableSpan): ReadableSpan { const normalized = {} as Record; for (const [key, value] of Object.entries(span.attributes)) { diff --git a/js/plugins/google-cloud/tests/traces_test.ts b/js/plugins/google-cloud/tests/traces_test.ts index e392e0875..a64e63243 100644 --- a/js/plugins/google-cloud/tests/traces_test.ts +++ b/js/plugins/google-cloud/tests/traces_test.ts @@ -171,6 +171,27 @@ describe('GoogleCloudTracing', () => { assert.equal(spans[1].attributes['genkit/rootState'], 'success'); }); + it('marks the root feature failed when it is the failure', async () => { + const testFlow = createFlow(ai, 'failingFlow', async () => { + await ai.run('good step', async () => {}); + throw new Error('oops!'); + }); + try { + await testFlow(); + } catch (e) {} + + const spans = await getExportedSpans(); + assert.equal(spans.length, 2); + assert.equal(spans[1].name, 'failingFlow'); + assert.equal(spans[1].attributes['genkit/failedSpan'], 'failingFlow'); + assert.equal( + spans[1].attributes['genkit/failedPath'], + '/{failingFlow,t:flow}' + ); + assert.equal(spans[1].attributes['genkit/isRoot'], true); + assert.equal(spans[1].attributes['genkit/rootState'], 'error'); + }); + it('adds the genkit/model label for model actions', async () => { const echoModel = ai.defineModel( { From a835c6451c2a44aff1a143598cb918c4bd1d3f78 Mon Sep 17 00:00:00 2001 From: thedmail Date: Fri, 17 Jan 2025 17:02:56 -0800 Subject: [PATCH 207/562] Update plugin-authoring-evaluator.md (#1619) Revisions to this file, as submitted in [PR #1512](https://github.com/firebase/genkit/pull/1512) --- docs/plugin-authoring-evaluator.md | 87 ++++++++++++++++-------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/docs/plugin-authoring-evaluator.md b/docs/plugin-authoring-evaluator.md index 732d855f8..031169fff 100644 --- a/docs/plugin-authoring-evaluator.md +++ b/docs/plugin-authoring-evaluator.md @@ -1,24 +1,27 @@ # Writing a Genkit Evaluator -Firebase Genkit can be extended to support custom evaluation, using either an +You can extend Firebase Genkit to support custom evaluation, using either an LLM as a judge, or by programmatic (heuristic) evaluation. ## Evaluator definition Evaluators are functions that assess an LLM's response. There are two main approaches to automated evaluation: heuristic evaluation and LLM-based -evaluation. In the heuristic approach, you define a deterministic function, -whereas in an LLM-based assessment, the content is fed back to an LLM and the -LLM is asked to score the output according to criteria set in a prompt. +evaluation. In the heuristic approach, you define a deterministic function. +By contrast, in an LLM-based assessment, the content is fed back to an LLM, +and the LLM is asked to score the output according to criteria set in a +prompt. -Both approaches are supported by the `ai.defineEvaluator` method to define an -evaluator action in Genkit. This document explores a couple of examples on how -to use this method for heuristic and LLM-based evaluations. +The `ai.defineEvaluator` method, which you use to define an +evaluator action in Genkit, supports either approach. This +document explores a couple of examples of how to use this +method for heuristic and LLM-based evaluations. -### LLM based Evaluators +### LLM-based Evaluators -An LLM-based evaluator leverages an LLM to evaluate the `input`, `context`, and -`output` of your generative AI feature. +An LLM-based evaluator leverages an LLM to evaluate +the `input`, `context`, and `output` of your generative AI +feature. LLM-based evaluators in Genkit are made up of 3 components: @@ -28,14 +31,14 @@ LLM-based evaluators in Genkit are made up of 3 components: #### Define the prompt -For this example, the evaluator leverages an LLM to determine whether an -`output` is delicious or not. First, provide context to the LLM, then describe -what you want it to do, and finally, give it a few examples to base its response -on. +For this example, the evaluator leverages an LLM to determine whether a +food (the `output`) is delicious or not. First, provide context to the LLM, +then describe what you want it to do, and finally, give it a few examples +to base its response on. Genkit’s `definePrompt` utility provides an easy way to define prompts with -input and output validation. You can set up an evaluation prompt with -`definePrompt` as follows: +input and output validation. The following code is an example of +setting up an evaluation prompt with `definePrompt`. ```ts import { z } from "genkit"; @@ -83,7 +86,7 @@ function getDeliciousnessPrompt(ai: Genkit) { #### Define the scoring function -Define a function that takes an example which includes `output` as it is +Define a function that takes an example that includes `output` as required by the prompt, and scores the result. Genkit testcases include `input` as a required field, with `output` and `context` as optional fields. It is the responsibility of the evaluator to validate that all fields required for @@ -172,13 +175,17 @@ export function createDeliciousnessEvaluator< ``` The `defineEvaluator` method is similar to other Genkit constructors like -`defineFlow`, `defineRetriever`, etc. This method requires an `EvaluatorFn` to -be provided as a callback method. The `EvaluatorFn` accepts a -`BaseEvalDataPoint` which corresponds to a single entry in a dataset under -evaluation, along with an optional custom options parameter if specified. The -function processes the datapoint and returns an `EvalResponse` object. +`defineFlow` and `defineRetriever`. This method requires an `EvaluatorFn` +to be provided as a callback. The `EvaluatorFn` method accepts a +`BaseEvalDataPoint` object, which corresponds to a single entry in a +dataset under evaluation, along with an optional custom-options +parameter if specified. The function processes the datapoint and +returns an `EvalResponse` object. -Here are the Zod Schemas for `BaseEvalDataPoint` and `EvalResponse`: +The Zod Schemas for `BaseEvalDataPoint` and `EvalResponse` are +as follows. + +##### `BaseEvalDataPoint` ```ts export const BaseEvalDataPoint = z.object({ @@ -199,7 +206,7 @@ export const EvalResponse = z.object({ evaluation: z.union([ScoreSchema, z.array(ScoreSchema)]), }); ``` -where `ScoreSchema` is defined as: +##### `ScoreSchema` ```ts const ScoreSchema = z.object({ @@ -215,12 +222,13 @@ const ScoreSchema = z.object({ }); ``` -`defineEvaluator` lets the user provide a name, a user-readable display name, -and a definition for the evaluator. The display name and definiton are displayed -along with evaluation results in the Dev UI. It also has an optional `isBilled` -option which marks whether this evaluator may result in billing (e.g.: it uses -a billed LLM or API). If an evaluator is billed, the user is prompted for a -confirmation in the CLI before they can run an evaluation, to help guard from +The `defineEvaluator` object lets the user provide a name, a user-readable +display name, and a definition for the evaluator. The display name and +definiton are displayed along with evaluation results in the Dev UI. +It also has an optional `isBilled` field that marks whether this evaluator +can result in billing (e.g., it uses a billed LLM or API). If an evaluator is +billed, the UI prompts the user for a confirmation in the CLI before +allowing them to run an evaluation. This step helps guard against unintended expenses. ### Heuristic Evaluators @@ -235,7 +243,7 @@ Heuristic evaluators in Genkit are made up of 2 components: #### Define the scoring function -Similar to the LLM-based evaluator, define the scoring function. In this case, +As with the LLM-based evaluator, define the scoring function. In this case, the scoring function does not need a judge LLM. ```ts @@ -302,9 +310,9 @@ Plugins are registered with the framework by installing them at the time of initializing Genkit. To define a new plugin, use the `genkitPlugin` helper method to instantiate all Genkit actions within the plugin context. -Here we have two evaluators,the LLM-based deliciousness evaluator and the -regex-based US phone number evaluator. Instatiating these evaluators within the -plugin context registeres them with the plugin. +This code sample shows two evaluators: the LLM-based deliciousness evaluator, +and the regex-based US phone number evaluator. Instatiating these +evaluators within the plugin context registers them with the plugin. ```ts import { GenkitPlugin, genkitPlugin } from 'genkit/plugin'; @@ -354,10 +362,11 @@ const ai = genkit({ ## Using your custom evaluators Once you instantiate your custom evaluators within the Genkit app context (either -through a plugin or directly), they are ready to be used. Let us try out the -deliciousness evaluator with a few sample inputs and outputs. +through a plugin or directly), they are ready to be used. The following example +illustrates how to try out the deliciousness evaluator with a few sample +inputs and outputs. -Create a json file `deliciousness_dataset.json` with the following content: +1. Create a json file `deliciousness_dataset.json` with the following content: ```json [ @@ -374,7 +383,7 @@ Create a json file `deliciousness_dataset.json` with the following content: ] ``` -Use the Genkit CLI to run the evaluator against these test cases. +2. Use the Genkit CLI to run the evaluator against these test cases. ```posix-terminal # Start your genkit runtime @@ -387,5 +396,5 @@ Navigate to `localhost:4000/evaluate` to view your results in the Genkit UI. It is important to note that confidence in custom evaluators increases as you benchmark them with standard datasets or approaches. Iterate on the results -of such benchmarks to improve your evaluators' performance till it reaches the +of such benchmarks to improve your evaluators' performance until it reaches the desired quality. From 3a30f31fc4f3f9b2676ae15b63458cb7d742434f Mon Sep 17 00:00:00 2001 From: Hugo Aguirre Date: Fri, 17 Jan 2025 19:53:25 -0600 Subject: [PATCH 208/562] fix(js): fix end to end tests (#1605) --- .github/workflows/e2e-tests.yml | 40 +++++++++++++++++---------------- tests/test_js_app/src/index.ts | 2 +- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 92f369d92..731744efc 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -24,22 +24,24 @@ jobs: name: Run e2e tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v3 - - name: Set up node v20 - uses: actions/setup-node@v4 - with: - node-version: 21.x - cache: 'pnpm' - - name: Install dependencies - run: pnpm install - - name: Run build script - run: pnpm build - - name: Run pack:all script - run: pnpm pack:all - - name: Run js tests - run: pnpm test:js - - name: Run e2e tests - run: pnpm test:e2e - - name: Validate working directory is clean - run: .github/workflows/scripts/ensure-clean-working-tree.sh + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v3 + - name: Set up node v20 + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: "pnpm" + - name: Install dependencies + run: pnpm install + - name: Run build script + run: pnpm build + - name: Run pack:all script + run: pnpm pack:all + - name: Install local Genkit + run: pnpm link ./js/genkit + - name: Run js tests + run: pnpm test:js + - name: Run e2e tests + run: pnpm test:e2e + - name: Validate working directory is clean + run: .github/workflows/scripts/ensure-clean-working-tree.sh diff --git a/tests/test_js_app/src/index.ts b/tests/test_js_app/src/index.ts index 43ff62d6c..72b288645 100644 --- a/tests/test_js_app/src/index.ts +++ b/tests/test_js_app/src/index.ts @@ -62,7 +62,7 @@ export const testFlow = ai.defineFlow( ); // genkit flow:run streamy 5 -s -export const streamy = ai.defineStreamingFlow( +export const streamy = ai.defineFlow( { name: 'streamy', inputSchema: z.number(), From b77921968b16195ef91c78a19384afdb6a760bd1 Mon Sep 17 00:00:00 2001 From: Cleo Schneider Date: Tue, 21 Jan 2025 11:04:49 -0500 Subject: [PATCH 209/562] Log sessionId attribute when present (#1620) --- js/ai/src/chat.ts | 16 +- js/plugins/google-cloud/src/gcpLogger.ts | 22 +- .../google-cloud/src/telemetry/action.ts | 31 ++- .../google-cloud/src/telemetry/feature.ts | 31 ++- .../google-cloud/src/telemetry/generate.ts | 33 ++- js/plugins/google-cloud/src/telemetry/path.ts | 50 +++- .../google-cloud/tests/logs_session_test.ts | 220 ++++++++++++++++++ js/plugins/google-cloud/tests/traces_test.ts | 46 ++++ js/pnpm-lock.yaml | 1 + 9 files changed, 417 insertions(+), 33 deletions(-) create mode 100644 js/plugins/google-cloud/tests/logs_session_test.ts diff --git a/js/ai/src/chat.ts b/js/ai/src/chat.ts index a98aa4887..c02f77ffb 100644 --- a/js/ai/src/chat.ts +++ b/js/ai/src/chat.ts @@ -15,7 +15,11 @@ */ import { StreamingCallback, z } from '@genkit-ai/core'; -import { SPAN_TYPE_ATTR, runInNewSpan } from '@genkit-ai/core/tracing'; +import { + ATTR_PREFIX, + SPAN_TYPE_ATTR, + runInNewSpan, +} from '@genkit-ai/core/tracing'; import { GenerateOptions, GenerateResponse, @@ -36,6 +40,8 @@ import { } from './session.js'; export const MAIN_THREAD = 'main'; +export const SESSION_ID_ATTR = `${ATTR_PREFIX}:sessionId`; +export const THREAD_NAME_ATTR = `${ATTR_PREFIX}:threadName`; export type ChatGenerateOptions< O extends z.ZodTypeAny = z.ZodTypeAny, @@ -143,8 +149,8 @@ export class Chat { }, labels: { [SPAN_TYPE_ATTR]: 'helper', - sessionId: this.session.id, - threadName: this.threadName, + [SESSION_ID_ATTR]: this.session.id, + [THREAD_NAME_ATTR]: this.threadName, }, }, async (metadata) => { @@ -205,8 +211,8 @@ export class Chat { metadata: { name: 'send' }, labels: { [SPAN_TYPE_ATTR]: 'helper', - sessionId: this.session.id, - threadName: this.threadName, + [SESSION_ID_ATTR]: this.session.id, + [THREAD_NAME_ATTR]: this.threadName, }, }, async (metadata) => { diff --git a/js/plugins/google-cloud/src/gcpLogger.ts b/js/plugins/google-cloud/src/gcpLogger.ts index 78989883d..ff95b821b 100644 --- a/js/plugins/google-cloud/src/gcpLogger.ts +++ b/js/plugins/google-cloud/src/gcpLogger.ts @@ -25,6 +25,7 @@ import { loggingDenied, loggingDeniedHelpText } from './utils.js'; * Additional streams for writing log data to. Useful for unit testing. */ let additionalStream: Writable; +let useJsonFormatOverride: boolean = false; /** * Provides a logger for exporting Genkit debug logs to GCP Cloud @@ -40,13 +41,15 @@ export class GcpLogger { // an internal OT warning and will result in logs not being // associated with correct spans/traces. const winston = await import('winston'); - const format = this.shouldExport(env) - ? { format: winston.format.json() } - : { - format: winston.format.printf((info): string => { - return `[${info.level}] ${info.message}`; - }), - }; + + const format = + useJsonFormatOverride || this.shouldExport(env) + ? { format: winston.format.json() } + : { + format: winston.format.printf((info): string => { + return `[${info.level}] ${info.message}`; + }), + }; let transports: any[] = []; transports.push( @@ -114,3 +117,8 @@ export class GcpLogger { export function __addTransportStreamForTesting(stream: Writable) { additionalStream = stream; } + +/** @hidden */ +export function __useJsonFormatForTesting() { + useJsonFormatOverride = true; +} diff --git a/js/plugins/google-cloud/src/telemetry/action.ts b/js/plugins/google-cloud/src/telemetry/action.ts index 5499ae386..adb75251b 100644 --- a/js/plugins/google-cloud/src/telemetry/action.ts +++ b/js/plugins/google-cloud/src/telemetry/action.ts @@ -81,11 +81,32 @@ class ActionTelemetry implements Telemetry { if (subtype === 'tool' && logIO) { const input = attributes['genkit:input'] as string; const output = attributes['genkit:output'] as string; + const sessionId = attributes['genkit:sessionId'] as string; + const threadName = attributes['genkit:threadName'] as string; + if (input) { - this.recordIO(span, 'Input', featureName, path, input, projectId); + this.recordIO( + span, + 'Input', + featureName, + path, + input, + projectId, + sessionId, + threadName + ); } if (output) { - this.recordIO(span, 'Output', featureName, path, output, projectId); + this.recordIO( + span, + 'Output', + featureName, + path, + output, + projectId, + sessionId, + threadName + ); } } } @@ -134,7 +155,9 @@ class ActionTelemetry implements Telemetry { featureName: string, qualifiedPath: string, input: string, - projectId?: string + projectId?: string, + sessionId?: string, + threadName?: string ) { const path = toDisplayPath(qualifiedPath); const sharedMetadata = { @@ -142,6 +165,8 @@ class ActionTelemetry implements Telemetry { path, qualifiedPath, featureName, + sessionId, + threadName, }; logger.logStructured(`${tag}[${path}, ${featureName}]`, { ...sharedMetadata, diff --git a/js/plugins/google-cloud/src/telemetry/feature.ts b/js/plugins/google-cloud/src/telemetry/feature.ts index 0763c2437..a893fba97 100644 --- a/js/plugins/google-cloud/src/telemetry/feature.ts +++ b/js/plugins/google-cloud/src/telemetry/feature.ts @@ -79,11 +79,32 @@ class FeaturesTelemetry implements Telemetry { if (logIO) { const input = attributes['genkit:input'] as string; const output = attributes['genkit:output'] as string; + const sessionId = attributes['genkit:sessionId'] as string; + const threadName = attributes['genkit:threadName'] as string; + if (input) { - this.recordIO(span, 'Input', name, path, input, projectId); + this.recordIO( + span, + 'Input', + name, + path, + input, + projectId, + sessionId, + threadName + ); } if (output) { - this.recordIO(span, 'Output', name, path, output, projectId); + this.recordIO( + span, + 'Output', + name, + path, + output, + projectId, + sessionId, + threadName + ); } } } @@ -121,7 +142,9 @@ class FeaturesTelemetry implements Telemetry { featureName: string, qualifiedPath: string, input: string, - projectId?: string + projectId?: string, + sessionId?: string, + threadName?: string ) { const path = toDisplayPath(qualifiedPath); const sharedMetadata = { @@ -129,6 +152,8 @@ class FeaturesTelemetry implements Telemetry { path, qualifiedPath, featureName, + sessionId, + threadName, }; logger.logStructured(`${tag}[${path}, ${featureName}]`, { ...sharedMetadata, diff --git a/js/plugins/google-cloud/src/telemetry/generate.ts b/js/plugins/google-cloud/src/telemetry/generate.ts index d56013a87..c1d5591ac 100644 --- a/js/plugins/google-cloud/src/telemetry/generate.ts +++ b/js/plugins/google-cloud/src/telemetry/generate.ts @@ -158,6 +158,9 @@ class GenerateTelemetry implements Telemetry { featureName = 'generate'; } + const sessionId = attributes['genkit:sessionId'] as string; + const threadName = attributes['genkit:threadName'] as string; + if (input) { this.recordGenerateActionMetrics(modelName, featureName, path, input, { response: output, @@ -169,7 +172,9 @@ class GenerateTelemetry implements Telemetry { featureName, path, input, - projectId + projectId, + sessionId, + threadName ); if (logIO) { @@ -179,7 +184,9 @@ class GenerateTelemetry implements Telemetry { featureName, path, input, - projectId + projectId, + sessionId, + threadName ); } } @@ -191,7 +198,9 @@ class GenerateTelemetry implements Telemetry { featureName, path, output, - projectId + projectId, + sessionId, + threadName ); } } @@ -222,7 +231,9 @@ class GenerateTelemetry implements Telemetry { featureName: string, qualifiedPath: string, input: GenerateRequestData, - projectId?: string + projectId?: string, + sessionId?: string, + threadName?: string ) { const path = toDisplayPath(qualifiedPath); const sharedMetadata = { @@ -231,6 +242,8 @@ class GenerateTelemetry implements Telemetry { path, qualifiedPath, featureName, + sessionId, + threadName, }; logger.logStructured(`Config[${path}, ${model}]`, { ...sharedMetadata, @@ -250,7 +263,9 @@ class GenerateTelemetry implements Telemetry { featureName: string, qualifiedPath: string, input: GenerateRequestData, - projectId?: string + projectId?: string, + sessionId?: string, + threadName?: string ) { const path = toDisplayPath(qualifiedPath); const sharedMetadata = { @@ -259,6 +274,8 @@ class GenerateTelemetry implements Telemetry { path, qualifiedPath, featureName, + sessionId, + threadName, }; const messages = input.messages.length; @@ -284,7 +301,9 @@ class GenerateTelemetry implements Telemetry { featureName: string, qualifiedPath: string, output: GenerateResponseData, - projectId?: string + projectId?: string, + sessionId?: string, + threadName?: string ) { const path = toDisplayPath(qualifiedPath); const sharedMetadata = { @@ -293,6 +312,8 @@ class GenerateTelemetry implements Telemetry { path, qualifiedPath, featureName, + sessionId, + threadName, }; const message = output.message || output.candidates?.[0]?.message!; diff --git a/js/plugins/google-cloud/src/telemetry/path.ts b/js/plugins/google-cloud/src/telemetry/path.ts index 8c71810e9..411a7d2c5 100644 --- a/js/plugins/google-cloud/src/telemetry/path.ts +++ b/js/plugins/google-cloud/src/telemetry/path.ts @@ -59,13 +59,25 @@ class PathsTelemetry implements Telemetry { const attributes = span.attributes; const name = attributes['genkit:name'] as string; const path = attributes['genkit:path'] as string; + const sessionId = attributes['genkit:sessionId'] as string; + const threadName = attributes['genkit:threadName'] as string; + const latencyMs = hrTimeToMilliseconds( hrTimeDuration(span.startTime, span.endTime) ); const state = attributes['genkit:state'] as string; if (state === 'success') { - this.writePathSuccess(span, paths!, name, path, latencyMs, projectId); + this.writePathSuccess( + span, + paths!, + name, + path, + latencyMs, + projectId, + sessionId, + threadName + ); return; } @@ -81,7 +93,9 @@ class PathsTelemetry implements Telemetry { path, latencyMs, errorName, - projectId + projectId, + sessionId, + threadName ); this.recordError( span, @@ -89,7 +103,9 @@ class PathsTelemetry implements Telemetry { errorName, errorMessage, errorStack, - projectId + projectId, + sessionId, + threadName ); return; } @@ -103,7 +119,9 @@ class PathsTelemetry implements Telemetry { errorName: string, errorMessage: string, errorStack: string, - projectId?: string + projectId?: string, + sessionId?: string, + threadName?: string ) { const displayPath = toDisplayPath(path); logger.logStructuredError(`Error[${displayPath}, ${errorName}]`, { @@ -115,6 +133,8 @@ class PathsTelemetry implements Telemetry { stack: errorStack, source: 'ts', sourceVersion: GENKIT_VERSION, + sessionId, + threadName, }); } @@ -124,7 +144,9 @@ class PathsTelemetry implements Telemetry { featureName: string, path: string, latencyMs: number, - projectId?: string + projectId?: string, + sessionId?: string, + threadName?: string ) { this.writePathMetrics( span, @@ -133,7 +155,9 @@ class PathsTelemetry implements Telemetry { featureName, latencyMs, undefined, - projectId + projectId, + sessionId, + threadName ); } @@ -144,7 +168,9 @@ class PathsTelemetry implements Telemetry { path: string, latencyMs: number, errorName: string, - projectId?: string + projectId?: string, + sessionId?: string, + threadName?: string ) { this.writePathMetrics( span, @@ -153,7 +179,9 @@ class PathsTelemetry implements Telemetry { featureName, latencyMs, errorName, - projectId + projectId, + sessionId, + threadName ); } @@ -165,7 +193,9 @@ class PathsTelemetry implements Telemetry { featureName: string, latencyMs: number, err?: string, - projectId?: string + projectId?: string, + sessionId?: string, + threadName?: string ) { const flowPaths = Array.from(paths).filter((meta) => meta.path.includes(featureName) @@ -175,6 +205,8 @@ class PathsTelemetry implements Telemetry { logger.logStructured(`Paths[${featureName}]`, { ...createCommonLogAttributes(span, projectId), flowName: featureName, + sessionId, + threadName, paths: flowPaths.map((p) => toDisplayPath(p.path)), }); diff --git a/js/plugins/google-cloud/tests/logs_session_test.ts b/js/plugins/google-cloud/tests/logs_session_test.ts new file mode 100644 index 000000000..53552a971 --- /dev/null +++ b/js/plugins/google-cloud/tests/logs_session_test.ts @@ -0,0 +1,220 @@ +/** + * Copyright 2024 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 { + afterAll, + beforeAll, + beforeEach, + describe, + expect, + it, + jest, +} from '@jest/globals'; +import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { GenerateResponseData, Genkit, genkit, z } from 'genkit'; +import { ModelAction } from 'genkit/model'; +import assert from 'node:assert'; +import { Writable } from 'stream'; +import { + __addTransportStreamForTesting, + __forceFlushSpansForTesting, + __getSpanExporterForTesting, + __useJsonFormatForTesting, + enableGoogleCloudTelemetry, +} from '../src/index.js'; + +jest.mock('../src/auth.js', () => { + const original = jest.requireActual('../src/auth.js'); + return { + ...(original || {}), + resolveCurrentPrincipal: jest.fn().mockImplementation(() => { + return Promise.resolve({ + projectId: 'test', + serviceAccountEmail: 'test@test.com', + }); + }), + credentialsFromEnvironment: jest.fn().mockImplementation(() => { + return Promise.resolve({ + projectId: 'test', + credentials: { + client_email: 'test@genkit.com', + private_key: '-----BEGIN PRIVATE KEY-----', + }, + }); + }), + }; +}); + +describe('GoogleCloudLogs for sessions', () => { + let logLines = ''; + const logStream = new Writable(); + logStream._write = (chunk, encoding, next) => { + logLines = logLines += chunk.toString(); + next(); + }; + + let ai: Genkit; + let testModel: ModelAction; + + beforeAll(async () => { + process.env.GCLOUD_PROJECT = 'test'; + process.env.GENKIT_ENV = 'dev'; + __useJsonFormatForTesting(); + __addTransportStreamForTesting(logStream); + + await enableGoogleCloudTelemetry({ + projectId: 'test', + forceDevExport: false, + metricExportIntervalMillis: 100, + metricExportTimeoutMillis: 100, + }); + + ai = genkit({ + // Force GCP Plugin to use in-memory metrics exporter + plugins: [], + }); + + testModel = createModel(ai, 'testModel', async () => { + return { + message: { + role: 'user', + content: [ + { + text: 'response', + }, + ], + }, + finishReason: 'stop', + usage: { + inputTokens: 10, + outputTokens: 14, + inputCharacters: 8, + outputCharacters: 16, + inputImages: 1, + outputImages: 3, + }, + }; + }); + + await waitForLogsInit(ai, logLines); + }); + + beforeEach(async () => { + logLines = ''; + __getSpanExporterForTesting().reset(); + }); + afterAll(async () => { + await ai.stopServers(); + }); + + it('writes logs with sessionId', async () => { + const chat = ai.chat(); + + await chat.send({ model: testModel, prompt: 'Test message' }); + + await getExportedSpans(); + + const logMessages = await getLogs(1, 100, logLines); + const logObjects = logMessages.map((l) => JSON.parse(l as string)); + + // Right now sessionId is applied only at the top level send. + // We intend to eventually make the session id available on all relevant spans. + logObjects.forEach((logBlob) => { + if ( + logBlob.message === 'Input[send, send]' || + logBlob.message === 'Output[send, send]' || + logBlob.message === 'Paths[send]' + ) { + expect(logBlob.sessionId).not.toBeUndefined(); + expect(logBlob.threadName).not.toBeUndefined(); + return; + } + + expect(logBlob.sessionId).toBeUndefined(); + expect(logBlob.threadName).toBeUndefined(); + }); + }); +}); + +/** Helper to create a flow with no inputs or outputs */ +function createFlow( + ai: Genkit, + name: string, + fn: () => Promise = async () => {} +) { + return ai.defineFlow( + { + name, + inputSchema: z.void(), + outputSchema: z.void(), + }, + fn + ); +} + +/** + * Helper to create a model that returns the value produced by the given + * response function. + */ +function createModel( + genkit: Genkit, + name: string, + respFn: () => Promise +) { + return genkit.defineModel({ name }, (req) => respFn()); +} + +async function waitForLogsInit(genkit: Genkit, logLines: any) { + await import('winston'); + const testFlow = createFlow(genkit, 'testFlow'); + await testFlow(); + await getLogs(1, 100, logLines); +} + +async function getLogs( + logCount: number, + maxAttempts: number, + logLines: string +): Promise { + var attempts = 0; + while (attempts++ < maxAttempts) { + await new Promise((resolve) => setTimeout(resolve, 100)); + const found = logLines + .trim() + .split('\n') + .map((l) => l.trim()); + if (found.length >= logCount) { + return found.filter((l) => l !== undefined && l !== ''); + } + } + assert.fail(`Waiting for logs, but none have been written.`); +} + +/** Polls the in memory metric exporter until the genkit scope is found. */ +async function getExportedSpans( + maxAttempts: number = 200 +): Promise { + __forceFlushSpansForTesting(); + var attempts = 0; + while (attempts++ < maxAttempts) { + await new Promise((resolve) => setTimeout(resolve, 50)); + const found = __getSpanExporterForTesting().getFinishedSpans(); + if (found.length > 0) { + return found; + } + } + assert.fail(`Timed out while waiting for spans to be exported.`); +} diff --git a/js/plugins/google-cloud/tests/traces_test.ts b/js/plugins/google-cloud/tests/traces_test.ts index a64e63243..47fc9b666 100644 --- a/js/plugins/google-cloud/tests/traces_test.ts +++ b/js/plugins/google-cloud/tests/traces_test.ts @@ -19,6 +19,7 @@ import { beforeAll, beforeEach, describe, + expect, it, jest, } from '@jest/globals'; @@ -254,6 +255,51 @@ describe('GoogleCloudTracing', () => { ); }); + it('writes sessionId and threadName for chats', async () => { + const testModel = ai.defineModel({ name: 'testModel' }, async () => { + return { + message: { + role: 'user', + content: [ + { + text: 'response', + }, + ], + }, + finishReason: 'stop', + usage: { + inputTokens: 10, + outputTokens: 14, + inputCharacters: 8, + outputCharacters: 16, + inputImages: 1, + outputImages: 3, + }, + }; + }); + + const chat = ai.chat(); + + await chat.send({ model: testModel, prompt: 'Sending test prompt' }); + + const spans = await getExportedSpans(); + // We should get 3 spans from this chat -- "send" which delegates to "generate" which delegates to our "testModel" + // Only the top level span will have the sessionId and threadName until we make sessionId more ubiquitous + expect(spans).toHaveLength(3); + + spans.forEach((span) => { + if (span.name === 'send') { + expect(span?.attributes['genkit/sessionId']).not.toBeUndefined(); + expect(span?.attributes['genkit/threadName']).not.toBeUndefined(); + return; + } + + // Once we make the change to have sessionId on all relevant spans, then these should verify they are populated. + expect(span?.attributes['genkit/sessionId']).toBeUndefined(); + expect(span?.attributes['genkit/threadName']).toBeUndefined(); + }); + }); + /** Helper to create a flow with no inputs or outputs */ function createFlow( ai: Genkit, diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 2db426433..42a12bba8 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -12279,6 +12279,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.25.7) + esbuild: 0.24.0 ts-jest@29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)))(typescript@5.6.3): dependencies: From 91190906ded26ac0ad3c7a9561b5316bc378faa2 Mon Sep 17 00:00:00 2001 From: Ali Ghaznavi <55113970+mghaznav@users.noreply.github.com> Date: Tue, 21 Jan 2025 14:21:43 -0500 Subject: [PATCH 210/562] Make the telemetry instructions more clear. (#1627) Clarify the language to indicate that the Firebase plugin and the Google Cloud plugin share configuration options. --- docs/plugins/firebase.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/plugins/firebase.md b/docs/plugins/firebase.md index ba5901ee1..6aa4967b5 100644 --- a/docs/plugins/firebase.md +++ b/docs/plugins/firebase.md @@ -63,17 +63,19 @@ To provide Firebase credentials, you also need to set up Google Cloud Applicatio ### Telemetry -The plugin has a direct dependency on the [Google Cloud plugin](google-cloud.md) and thus has provisions to enable telemetry export to Google's Cloud operations suite. To enable telemetry export call `enableFirebaseTelemetry()`: +To enable telemetry export to Google Cloud's operations suite call `enableFirebaseTelemetry()`: ```js import { enableFirebaseTelemetry } from '@genkit-ai/firebase'; -enableFirebaseTelemetry(); +enableFirebaseTelemetry({ + forceDevExport: false, // Set this to true to export telemetry for local runs +}); ``` -Refer to the [Google Cloud plugin](google-cloud.md) documentation for all configuration options and the necessary APIs that need to be enabled on the project. +This plugin shares [configuration options](google-cloud.md#plugin-configuration) with the [Google Cloud plugin](google-cloud.md). ### Cloud Firestore vector search From f0e991b417690b67595adce33b67b12ae18181f9 Mon Sep 17 00:00:00 2001 From: Ali Ghaznavi <55113970+mghaznav@users.noreply.github.com> Date: Tue, 21 Jan 2025 14:22:14 -0500 Subject: [PATCH 211/562] Make observability docs more opinionated. (#1628) Remove mentions of Google Cloud plugin from the main observability and monitoring page in order to direct users to using the Firebase plugin. This more closely aligns with Genkit branding. --- docs/monitoring.md | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/docs/monitoring.md b/docs/monitoring.md index 5a920277f..68770daee 100644 --- a/docs/monitoring.md +++ b/docs/monitoring.md @@ -5,16 +5,14 @@ Firebase Genkit is fully instrumented with ## Telemetry Configuration -Genkit automatically manages tracing and metrics without requiring explicit configuration. You can enable telemetry exports for Firebase or Google Cloud using their respective plugins and helper functions. Using either plugin powers the [Genkit Monitoring dashboard](https://console.firebase.google.com/project/_/genai_monitoring) that has an AI-centric view of telemetry data. - -### For Firebase: +Genkit automatically manages tracing and metrics without requiring explicit configuration. You can enable telemetry exports to Firebase using the respective plugin and helper function. Using the Firebase plugin powers the [Genkit Monitoring dashboard](https://console.firebase.google.com/project/_/genai_monitoring) that has an AI-centric view of telemetry data. ```ts import { genkit } from 'genkit'; import { enableFirebaseTelemetry } from '@genkit-ai/firebase'; enableFirebaseTelemetry({ - // Firebase-specific configuration options + // Configuration options }); const ai = genkit({ @@ -23,22 +21,6 @@ const ai = genkit({ ``` More details are outlined in the [Firebase plugin docs](./plugins/firebase.md). -### For Google Cloud: - -```ts -import { genkit } from 'genkit'; -import { enableGoogleCloudTelemetry } from '@genkit-ai/google-cloud'; - -enableGoogleCloudTelemetry({ - // Google Cloud-specific configuration options -}); - -const ai = genkit({ - plugins: [ ... ] -}); -``` -More details are outlined in the [Google Cloud plugin docs](./plugins/google-cloud.md). - ## Logging Genkit provides a centralized logging system that can be configured using the logging module. Logs will be exported Google Cloud operations suite if telemetry export is enabled. From e49ed8925f8a39ccf20715c0bbfb85fb5b2d125d Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:45:00 -0500 Subject: [PATCH 212/562] fix(evals): make testcaseId and traceId optional in input formats, refactor (#1622) --- .../cli/src/commands/eval-extract-data.ts | 8 ++- genkit-tools/cli/src/commands/eval-flow.ts | 27 +++++--- genkit-tools/cli/src/commands/eval-run.ts | 12 +--- genkit-tools/common/src/eval/evaluate.ts | 67 ++++++++++--------- genkit-tools/common/src/eval/index.ts | 2 +- .../common/src/eval/localFileDatasetStore.ts | 8 +-- genkit-tools/common/src/types/apis.ts | 8 +-- genkit-tools/common/src/types/eval.ts | 36 ++++++++-- genkit-tools/common/src/utils/eval.ts | 58 ++++++++++------ js/tsconfig.json | 2 +- 10 files changed, 142 insertions(+), 86 deletions(-) diff --git a/genkit-tools/cli/src/commands/eval-extract-data.ts b/genkit-tools/cli/src/commands/eval-extract-data.ts index 509eb7fef..0ff79332f 100644 --- a/genkit-tools/cli/src/commands/eval-extract-data.ts +++ b/genkit-tools/cli/src/commands/eval-extract-data.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { EvalInput, TraceData } from '@genkit-ai/tools-common'; +import { + EvalInput, + EvalInputDataset, + TraceData, +} from '@genkit-ai/tools-common'; import { generateTestCaseId, getEvalExtractors, @@ -45,7 +49,7 @@ export const evalExtractData = new Command('eval:extractData') const extractors = await getEvalExtractors(`/flow/${flowName}`); logger.info(`Extracting trace data '/flow/${flowName}'...`); - let dataset: EvalInput[] = []; + let dataset: EvalInputDataset = []; let continuationToken = undefined; while (dataset.length < parseInt(options.maxRows)) { const response = await manager.listTraces({ diff --git a/genkit-tools/cli/src/commands/eval-flow.ts b/genkit-tools/cli/src/commands/eval-flow.ts index 8225b58a8..16b52affe 100644 --- a/genkit-tools/cli/src/commands/eval-flow.ts +++ b/genkit-tools/cli/src/commands/eval-flow.ts @@ -16,8 +16,9 @@ import { Action, - EvalInferenceInput, - EvalInferenceInputSchema, + Dataset, + DatasetMetadata, + DatasetSchema, } from '@genkit-ai/tools-common'; import { EvalExporter, @@ -30,7 +31,7 @@ import { } from '@genkit-ai/tools-common/eval'; import { confirmLlmUse, - loadEvalInference, + loadInferenceDatasetFile, logger, } from '@genkit-ai/tools-common/utils'; import { Command } from 'commander'; @@ -121,16 +122,20 @@ export const evalFlow = new Command('eval:flow') const datasetStore = await getDatasetStore(); const datasetMetadatas = await datasetStore.listDatasets(); targetDatasetMetadata = datasetMetadatas.find( - (d) => d.datasetId === options.input + (d: DatasetMetadata) => d.datasetId === options.input ); } const actionRef = `/flow/${flowName}`; - const evalFlowInput = await readInputs(sourceType, data, options.input); + const inferenceDataset = await readInputs( + sourceType, + data, + options.input + ); const evalDataset = await runInference({ manager, actionRef, - evalInferenceInput: evalFlowInput, + inferenceDataset, auth: options.auth, }); @@ -168,7 +173,7 @@ async function readInputs( sourceType: SourceType, dataField?: string, input?: string -): Promise { +): Promise { let parsedData; switch (sourceType) { case SourceType.DATA: @@ -176,7 +181,7 @@ async function readInputs( break; case SourceType.FILE: try { - return await loadEvalInference(input!); + return await loadInferenceDatasetFile(input!); } catch (e) { throw new Error(`Error parsing the input from file. Error: ${e}`); } @@ -188,7 +193,7 @@ async function readInputs( } try { - return EvalInferenceInputSchema.parse(parsedData); + return DatasetSchema.parse(parsedData); } catch (e) { throw new Error( `Error parsing the input. Please provide an array of inputs for the flow or a ${EVAL_FLOW_SCHEMA} object. Error: ${e}` @@ -201,7 +206,9 @@ function getSourceType(data?: string, input?: string): SourceType { if (data) { logger.warn('Both [data] and input provided, ignoring [data]...'); } - return input.endsWith('.json') ? SourceType.FILE : SourceType.DATASET; + return input.endsWith('.json') || input.endsWith('.jsonl') + ? SourceType.FILE + : SourceType.DATASET; } else if (data) { return SourceType.DATA; } diff --git a/genkit-tools/cli/src/commands/eval-run.ts b/genkit-tools/cli/src/commands/eval-run.ts index 48f3719c2..f09924d55 100644 --- a/genkit-tools/cli/src/commands/eval-run.ts +++ b/genkit-tools/cli/src/commands/eval-run.ts @@ -24,8 +24,7 @@ import { } from '@genkit-ai/tools-common/eval'; import { confirmLlmUse, - generateTestCaseId, - loadEvalInputDataset, + loadEvaluationDatasetFile, logger, } from '@genkit-ai/tools-common/utils'; import { Command } from 'commander'; @@ -92,13 +91,8 @@ export const evalRun = new Command('eval:run') } } - const evalDataset: EvalInputDataset = ( - await loadEvalInputDataset(dataset) - ).map((testCase: any) => ({ - ...testCase, - testCaseId: testCase.testCaseId || generateTestCaseId(), - traceIds: testCase.traceIds || [], - })); + const evalDataset: EvalInputDataset = + await loadEvaluationDatasetFile(dataset); const evalRun = await runEvaluation({ manager, evaluatorActions, diff --git a/genkit-tools/common/src/eval/evaluate.ts b/genkit-tools/common/src/eval/evaluate.ts index 1055a4636..da9f8a903 100644 --- a/genkit-tools/common/src/eval/evaluate.ts +++ b/genkit-tools/common/src/eval/evaluate.ts @@ -15,12 +15,13 @@ */ import { randomUUID } from 'crypto'; -import { EvalInferenceInput, getDatasetStore, getEvalStore } from '.'; +import { getDatasetStore, getEvalStore } from '.'; import { RuntimeManager } from '../manager/manager'; import { Action, CandidateData, - EvalInferenceInputSchema, + Dataset, + DatasetSchema, EvalInput, EvalKeyAugments, EvalRun, @@ -51,7 +52,7 @@ interface InferenceRunState { evalError?: string; } -interface TestCase { +interface FullInferenceSample { testCaseId: string; input: any; reference?: any; @@ -72,7 +73,7 @@ export async function runNewEvaluation( throw new Error(`Either 'data' or 'datasetId' must be provided`); } - let evalInferenceInput: EvalInferenceInput; + let inferenceDataset: Dataset; let metadata = {}; if (datasetId) { const datasetStore = await getDatasetStore(); @@ -81,7 +82,7 @@ export async function runNewEvaluation( if (dataset.length === 0) { throw new Error(`Dataset ${datasetId} is empty`); } - evalInferenceInput = EvalInferenceInputSchema.parse(dataset); + inferenceDataset = DatasetSchema.parse(dataset); const datasetMetadatas = await datasetStore.listDatasets(); const targetDatasetMetadata = datasetMetadatas.find( @@ -90,14 +91,18 @@ export async function runNewEvaluation( const datasetVersion = targetDatasetMetadata?.version; metadata = { datasetId, datasetVersion }; } else { - evalInferenceInput = data!; + const rawData = data!.map((sample) => ({ + ...sample, + testCaseId: sample.testCaseId ?? generateTestCaseId(), + })); + inferenceDataset = DatasetSchema.parse(rawData); } logger.info('Running inference...'); const evalDataset = await runInference({ manager, actionRef, - evalInferenceInput, + inferenceDataset, auth: request.options?.auth, actionConfig: request.options?.actionConfig, }); @@ -123,11 +128,11 @@ export async function runNewEvaluation( export async function runInference(params: { manager: RuntimeManager; actionRef: string; - evalInferenceInput: EvalInferenceInput; + inferenceDataset: Dataset; auth?: string; actionConfig?: any; }): Promise { - const { manager, actionRef, evalInferenceInput, auth, actionConfig } = params; + const { manager, actionRef, inferenceDataset, auth, actionConfig } = params; if (!isSupportedActionRef(actionRef)) { throw new Error('Inference is only supported on flows and models'); } @@ -135,7 +140,7 @@ export async function runInference(params: { const evalDataset: EvalInput[] = await bulkRunAction({ manager, actionRef, - evalInferenceInput, + inferenceDataset, auth, actionConfig, }); @@ -222,31 +227,31 @@ export async function getMatchingEvaluatorActions( async function bulkRunAction(params: { manager: RuntimeManager; actionRef: string; - evalInferenceInput: EvalInferenceInput; + inferenceDataset: Dataset; auth?: string; actionConfig?: any; }): Promise { - const { manager, actionRef, evalInferenceInput, auth, actionConfig } = params; + const { manager, actionRef, inferenceDataset, auth, actionConfig } = params; const isModelAction = actionRef.startsWith('/model'); - let testCases: TestCase[] = evalInferenceInput.map((c) => ({ - input: c.input, - reference: c.reference, - testCaseId: c.testCaseId ?? generateTestCaseId(), - })); - if (testCases.length === 0) { + if (inferenceDataset.length === 0) { throw new Error('Cannot run inference, no data provided'); } + // Convert to satisfy TS checks. `input` is required in `Dataset` type, but + // ZodAny also includes `undefined` in TS checks. This explcit conversion + // works around this. + const fullInferenceDataset = inferenceDataset as FullInferenceSample[]; + let states: InferenceRunState[] = []; let evalInputs: EvalInput[] = []; - for (const testCase of testCases) { + for (const sample of fullInferenceDataset) { logger.info(`Running inference '${actionRef}' ...`); if (isModelAction) { states.push( await runModelAction({ manager, actionRef, - testCase, + sample, modelConfig: actionConfig, }) ); @@ -255,7 +260,7 @@ async function bulkRunAction(params: { await runFlowAction({ manager, actionRef, - testCase, + sample, auth, }) ); @@ -272,26 +277,26 @@ async function bulkRunAction(params: { async function runFlowAction(params: { manager: RuntimeManager; actionRef: string; - testCase: TestCase; + sample: FullInferenceSample; auth?: any; }): Promise { - const { manager, actionRef, testCase, auth } = { ...params }; + const { manager, actionRef, sample, auth } = { ...params }; let state: InferenceRunState; try { const runActionResponse = await manager.runAction({ key: actionRef, - input: testCase.input, + input: sample.input, context: auth ? JSON.parse(auth) : undefined, }); state = { - ...testCase, + ...sample, traceId: runActionResponse.telemetry?.traceId, response: runActionResponse.result, }; } catch (e: any) { const traceId = e?.data?.details?.traceId; state = { - ...testCase, + ...sample, traceId, evalError: `Error when running inference. Details: ${e?.message ?? e}`, }; @@ -302,26 +307,26 @@ async function runFlowAction(params: { async function runModelAction(params: { manager: RuntimeManager; actionRef: string; - testCase: TestCase; + sample: FullInferenceSample; modelConfig?: any; }): Promise { - const { manager, actionRef, modelConfig, testCase } = { ...params }; + const { manager, actionRef, modelConfig, sample } = { ...params }; let state: InferenceRunState; try { - const modelInput = getModelInput(testCase.input, modelConfig); + const modelInput = getModelInput(sample.input, modelConfig); const runActionResponse = await manager.runAction({ key: actionRef, input: modelInput, }); state = { - ...testCase, + ...sample, traceId: runActionResponse.telemetry?.traceId, response: runActionResponse.result, }; } catch (e: any) { const traceId = e?.data?.details?.traceId; state = { - ...testCase, + ...sample, traceId, evalError: `Error when running inference. Details: ${e?.message ?? e}`, }; diff --git a/genkit-tools/common/src/eval/index.ts b/genkit-tools/common/src/eval/index.ts index 5cd4fadcb..d2805447a 100644 --- a/genkit-tools/common/src/eval/index.ts +++ b/genkit-tools/common/src/eval/index.ts @@ -17,7 +17,7 @@ import { DatasetStore, EvalStore } from '../types/eval'; import { LocalFileDatasetStore } from './localFileDatasetStore'; import { LocalFileEvalStore } from './localFileEvalStore'; -export { EvalInferenceInput, EvalInferenceInputSchema } from '../types/eval'; +export { InferenceDataset, InferenceDatasetSchema } from '../types/eval'; export * from './evaluate'; export * from './exporter'; export * from './parser'; diff --git a/genkit-tools/common/src/eval/localFileDatasetStore.ts b/genkit-tools/common/src/eval/localFileDatasetStore.ts index b21408f59..c025d26c3 100644 --- a/genkit-tools/common/src/eval/localFileDatasetStore.ts +++ b/genkit-tools/common/src/eval/localFileDatasetStore.ts @@ -24,7 +24,7 @@ import { DatasetMetadata, DatasetSchema, DatasetStore, - EvalInferenceInput, + InferenceDataset, } from '../types/eval'; import { generateTestCaseId } from '../utils'; import { logger } from '../utils/logger'; @@ -81,7 +81,7 @@ export class LocalFileDatasetStore implements DatasetStore { `Create dataset failed: file already exists at {$filePath}` ); } - const dataset = this.getDatasetFromInferenceInput(data); + const dataset = this.getDatasetFromInferenceDataset(data); logger.info(`Saving Dataset to ` + filePath); await writeFile(filePath, JSON.stringify(dataset)); @@ -123,7 +123,7 @@ export class LocalFileDatasetStore implements DatasetStore { if (!prevMetadata) { throw new Error(`Update dataset failed: dataset metadata not found`); } - const patch = this.getDatasetFromInferenceInput(data ?? []); + const patch = this.getDatasetFromInferenceDataset(data ?? []); let newSize = prevMetadata.size; if (patch.length > 0) { logger.info(`Updating Dataset at ` + filePath); @@ -242,7 +242,7 @@ export class LocalFileDatasetStore implements DatasetStore { ); } - private getDatasetFromInferenceInput(data: EvalInferenceInput): Dataset { + private getDatasetFromInferenceDataset(data: InferenceDataset): Dataset { return data.map((d) => ({ testCaseId: d.testCaseId ?? generateTestCaseId(), ...d, diff --git a/genkit-tools/common/src/types/apis.ts b/genkit-tools/common/src/types/apis.ts index 430296929..f9a34cc25 100644 --- a/genkit-tools/common/src/types/apis.ts +++ b/genkit-tools/common/src/types/apis.ts @@ -18,8 +18,8 @@ import { z } from 'zod'; import { DatasetSchemaSchema, DatasetTypeSchema, - EvalInferenceInputSchema, EvalRunKeySchema, + InferenceDatasetSchema, } from './eval'; import { GenerationCommonConfigSchema, @@ -113,7 +113,7 @@ export const GetEvalRunRequestSchema = z.object({ export type GetEvalRunRequest = z.infer; export const CreateDatasetRequestSchema = z.object({ - data: EvalInferenceInputSchema, + data: InferenceDatasetSchema, datasetId: z.string().optional(), datasetType: DatasetTypeSchema, schema: DatasetSchemaSchema.optional(), @@ -124,7 +124,7 @@ export type CreateDatasetRequest = z.infer; export const UpdateDatasetRequestSchema = z.object({ datasetId: z.string(), - data: EvalInferenceInputSchema.optional(), + data: InferenceDatasetSchema.optional(), schema: DatasetSchemaSchema.optional(), targetAction: z.string().optional(), }); @@ -133,7 +133,7 @@ export type UpdateDatasetRequest = z.infer; export const RunNewEvaluationRequestSchema = z.object({ dataSource: z.object({ datasetId: z.string().optional(), - data: EvalInferenceInputSchema.optional(), + data: InferenceDatasetSchema.optional(), }), actionRef: z.string(), evaluators: z.array(z.string()).optional(), diff --git a/genkit-tools/common/src/types/eval.ts b/genkit-tools/common/src/types/eval.ts index 57782b42f..b75ad091d 100644 --- a/genkit-tools/common/src/types/eval.ts +++ b/genkit-tools/common/src/types/eval.ts @@ -59,22 +59,23 @@ export const GenerateRequestJSONSchema = zodToJsonSchema( /** * A single sample to be used for inference. **/ -export const EvalInferenceSampleSchema = z.object({ +export const InferenceSampleSchema = z.object({ testCaseId: z.string().optional(), input: z.any(), reference: z.any().optional(), }); +export type InferenceSample = z.infer; /** * A set of samples that is ready for inference. * * This should be used in user-facing surfaces (CLI/API inputs) to accommodate various user input formats. For internal wire-transfer/storage, prefer {@link Dataset}. */ -export const EvalInferenceInputSchema = z.array(EvalInferenceSampleSchema); -export type EvalInferenceInput = z.infer; +export const InferenceDatasetSchema = z.array(InferenceSampleSchema); +export type InferenceDataset = z.infer; /** - * Represents a Dataset, to be used for bulk-inference / evaluation. This is a more optionated form of EvalInferenceInput, which guarantees testCaseId for each test sample. + * Represents a Dataset, to be used for bulk-inference / evaluation. This is a more optionated form of InferenceDataset, which guarantees testCaseId for each test sample. */ export const DatasetSchema = z.array( z.object({ @@ -88,7 +89,32 @@ export type Dataset = z.infer; /** * A record that is ready for evaluation. * - * TODO: consider renaming. + * This should be used in user-facing surfaces (CLI/API inputs) to accommodate various user input formats. For use in EvaluatorAction, use {@link EvalInput}. + */ +export const EvaluationSampleSchema = z.object({ + testCaseId: z.string().optional(), + input: z.any(), + output: z.any(), + error: z.string().optional(), + context: z.array(z.any()).optional(), + reference: z.any().optional(), + traceIds: z.array(z.string()).optional(), +}); +export type EvaluationSample = z.infer; + +/** + * A set of samples that is ready for evaluation. + * + * This should be used in user-facing surfaces (CLI/API inputs) to accommodate various user input formats. For use in EvaluatorAction, use {@link EvalInput}. + */ +export const EvaluationDatasetSchema = z.array(EvaluationSampleSchema); +export type EvaluationDataset = z.infer; + +/** + * A fully-valid record to be used for evaluation. + * + * This is not user facing. Required fields that are missing from + * {@link EvaluationSampleSchema} must be auto-popoulated. */ export const EvalInputSchema = z.object({ testCaseId: z.string(), diff --git a/genkit-tools/common/src/utils/eval.ts b/genkit-tools/common/src/utils/eval.ts index 08023fc04..1ba74f224 100644 --- a/genkit-tools/common/src/utils/eval.ts +++ b/genkit-tools/common/src/utils/eval.ts @@ -29,12 +29,16 @@ import { isEvalField, } from '../plugin'; import { - EvalInferenceInput, - EvalInferenceInputSchema, - EvalInferenceSampleSchema, + Dataset, + DatasetSchema, EvalInputDataset, EvalInputDatasetSchema, - EvalInputSchema, + EvaluationDatasetSchema, + EvaluationSample, + EvaluationSampleSchema, + InferenceDatasetSchema, + InferenceSample, + InferenceSampleSchema, } from '../types'; import { Action } from '../types/action'; import { DocumentData, RetrieverResponse } from '../types/retrievers'; @@ -234,22 +238,27 @@ export function generateTestCaseId() { return randomUUID(); } -/** Load a {@link EvalInferenceInput} file. Supports JSON / JSONL */ -export async function loadEvalInference( +/** Load a {@link Dataset} file. Supports JSON / JSONL */ +export async function loadInferenceDatasetFile( fileName: string -): Promise { +): Promise { const isJsonl = fileName.endsWith('.jsonl'); if (isJsonl) { return await readJsonlForInference(fileName); } else { const parsedData = JSON.parse(await readFile(fileName, 'utf8')); - return EvalInferenceInputSchema.parse(parsedData); + let dataset = InferenceDatasetSchema.parse(parsedData); + dataset = dataset.map((sample: InferenceSample) => ({ + ...sample, + testCaseId: sample.testCaseId ?? generateTestCaseId(), + })); + return DatasetSchema.parse(dataset); } } -/** Load a {@link Eval} file. Supports JSON / JSONL */ -export async function loadEvalInputDataset( +/** Load a {@link EvalInputDataset} file. Supports JSON / JSONL */ +export async function loadEvaluationDatasetFile( fileName: string ): Promise { const isJsonl = fileName.endsWith('.jsonl'); @@ -258,18 +267,25 @@ export async function loadEvalInputDataset( return await readJsonlForEvaluation(fileName); } else { const parsedData = JSON.parse(await readFile(fileName, 'utf8')); - return EvalInputDatasetSchema.parse(parsedData); + let evaluationInput = EvaluationDatasetSchema.parse(parsedData); + evaluationInput = evaluationInput.map((evalSample: EvaluationSample) => ({ + ...evalSample, + testCaseId: evalSample.testCaseId ?? generateTestCaseId(), + traceIds: evalSample.traceIds ?? [], + })); + return EvalInputDatasetSchema.parse(evaluationInput); } } -async function readJsonlForInference( - fileName: string -): Promise { +async function readJsonlForInference(fileName: string): Promise { const lines = await readLines(fileName); - const samples: EvalInferenceInput = []; + const samples: Dataset = []; for (const line of lines) { - const parsedSample = EvalInferenceSampleSchema.parse(JSON.parse(line)); - samples.push(parsedSample); + const parsedSample = InferenceSampleSchema.parse(JSON.parse(line)); + samples.push({ + ...parsedSample, + testCaseId: parsedSample.testCaseId ?? generateTestCaseId(), + }); } return samples; } @@ -280,8 +296,12 @@ async function readJsonlForEvaluation( const lines = await readLines(fileName); const inputs: EvalInputDataset = []; for (const line of lines) { - const parsedSample = EvalInputSchema.parse(JSON.parse(line)); - inputs.push(parsedSample); + const parsedSample = EvaluationSampleSchema.parse(JSON.parse(line)); + inputs.push({ + ...parsedSample, + testCaseId: parsedSample.testCaseId ?? generateTestCaseId(), + traceIds: parsedSample.traceIds ?? [], + }); } return inputs; } diff --git a/js/tsconfig.json b/js/tsconfig.json index f97e2100c..521b487b7 100644 --- a/js/tsconfig.json +++ b/js/tsconfig.json @@ -12,7 +12,7 @@ "esModuleInterop": true, "strict": true, "target": "es2022", - "lib": ["es2022"], + "lib": ["es2022", "DOM"], "noEmit": true }, "compileOnSave": true From bd7aa3de2efa81165e9b69aac993bb250a5ad9d0 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 21 Jan 2025 17:19:38 -0800 Subject: [PATCH 213/562] Remove esModuleInterop as it cascades to a customer requirement (#1635) --- .../tests/firestoreTraceStore_test.ts | 2 +- js/ai/src/testing/model-tester.ts | 2 +- js/ai/tests/extract_test.ts | 2 +- js/ai/tests/formats/array_test.ts | 2 +- js/ai/tests/formats/enum_test.ts | 2 +- js/ai/tests/formats/json_test.ts | 2 +- js/ai/tests/formats/jsonl_test.ts | 2 +- js/ai/tests/formats/text_test.ts | 2 +- js/ai/tests/generate/chunk_test.ts | 2 +- js/ai/tests/generate/generate_test.ts | 2 +- js/ai/tests/generate/response_test.ts | 2 +- js/ai/tests/message/message_test.ts | 2 +- js/ai/tests/model/document_test.ts | 2 +- js/ai/tests/model/middleware_test.ts | 4 ++-- js/ai/tests/prompt/prompt_test.ts | 2 +- js/ai/tests/reranker/reranker_test.ts | 12 ++++++------ js/core/src/reflection.ts | 2 +- js/core/tests/action_test.ts | 2 +- js/core/tests/flow_test.ts | 2 +- js/core/tests/registry_test.ts | 2 +- js/core/tests/schema_test.ts | 2 +- js/genkit/tests/chat_test.ts | 2 +- js/genkit/tests/embed_test.ts | 2 +- js/genkit/tests/evaluate_test.ts | 2 +- js/genkit/tests/flow_test.ts | 2 +- js/genkit/tests/formats_test.ts | 2 +- js/genkit/tests/generate_test.ts | 2 +- js/genkit/tests/prompts_test.ts | 2 +- js/genkit/tests/session_test.ts | 2 +- js/plugins/dotprompt/src/template.ts | 2 +- js/plugins/dotprompt/tests/picoschema_test.ts | 2 +- js/plugins/dotprompt/tests/prompt_test.ts | 2 +- js/plugins/dotprompt/tests/template_test.ts | 2 +- js/plugins/express/tests/express_test.ts | 2 +- js/plugins/firebase/tests/functions_test.ts | 4 ++-- js/plugins/google-cloud/package.json | 2 +- js/plugins/google-cloud/tests/logs_no_io_test.ts | 2 +- js/plugins/google-cloud/tests/logs_session_test.ts | 2 +- js/plugins/google-cloud/tests/logs_test.ts | 2 +- js/plugins/google-cloud/tests/metrics_test.ts | 2 +- js/plugins/google-cloud/tests/traces_test.ts | 2 +- js/plugins/googleai/tests/gemini_test.ts | 2 +- js/plugins/ollama/tests/embedding_live_test.ts | 2 +- js/plugins/ollama/tests/embeddings_test.ts | 4 ++-- js/plugins/vertexai/tests/gemini_test.ts | 2 +- .../vertexai/tests/modelgarden/anthropic_test.ts | 2 +- .../vertexai/tests/modelgarden/mistral_test.ts | 2 +- .../vertexai/tests/vectorsearch/bigquery_test.ts | 2 +- js/tsconfig.json | 5 ++++- 49 files changed, 60 insertions(+), 57 deletions(-) diff --git a/genkit-tools/telemetry-server/tests/firestoreTraceStore_test.ts b/genkit-tools/telemetry-server/tests/firestoreTraceStore_test.ts index 3fdc38a83..3a3e4bfa8 100644 --- a/genkit-tools/telemetry-server/tests/firestoreTraceStore_test.ts +++ b/genkit-tools/telemetry-server/tests/firestoreTraceStore_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { rebatchSpans } from '../src/firestoreTraceStore'; diff --git a/js/ai/src/testing/model-tester.ts b/js/ai/src/testing/model-tester.ts index a4f50a496..4f6dc7ab6 100644 --- a/js/ai/src/testing/model-tester.ts +++ b/js/ai/src/testing/model-tester.ts @@ -17,7 +17,7 @@ import { z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; import { runInNewSpan } from '@genkit-ai/core/tracing'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { generate } from '../generate'; import { ModelAction } from '../model'; import { defineTool } from '../tool'; diff --git a/js/ai/tests/extract_test.ts b/js/ai/tests/extract_test.ts index 98d3018a8..06812ebc0 100644 --- a/js/ai/tests/extract_test.ts +++ b/js/ai/tests/extract_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { extractItems, extractJson, parsePartialJson } from '../src/extract'; diff --git a/js/ai/tests/formats/array_test.ts b/js/ai/tests/formats/array_test.ts index e85729b31..5264b9ce1 100644 --- a/js/ai/tests/formats/array_test.ts +++ b/js/ai/tests/formats/array_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { arrayFormatter } from '../../src/formats/array.js'; import { GenerateResponseChunk } from '../../src/generate.js'; diff --git a/js/ai/tests/formats/enum_test.ts b/js/ai/tests/formats/enum_test.ts index 299156f4d..061513c16 100644 --- a/js/ai/tests/formats/enum_test.ts +++ b/js/ai/tests/formats/enum_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { enumFormatter } from '../../src/formats/enum.js'; import { Message } from '../../src/message.js'; diff --git a/js/ai/tests/formats/json_test.ts b/js/ai/tests/formats/json_test.ts index 6a8e94420..9dd5723a3 100644 --- a/js/ai/tests/formats/json_test.ts +++ b/js/ai/tests/formats/json_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { jsonFormatter } from '../../src/formats/json.js'; import { GenerateResponseChunk } from '../../src/generate.js'; diff --git a/js/ai/tests/formats/jsonl_test.ts b/js/ai/tests/formats/jsonl_test.ts index a5f3bce77..e010c4df6 100644 --- a/js/ai/tests/formats/jsonl_test.ts +++ b/js/ai/tests/formats/jsonl_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { jsonlFormatter } from '../../src/formats/jsonl.js'; import { GenerateResponseChunk } from '../../src/generate.js'; diff --git a/js/ai/tests/formats/text_test.ts b/js/ai/tests/formats/text_test.ts index 2c9fecd5f..2b1794d37 100644 --- a/js/ai/tests/formats/text_test.ts +++ b/js/ai/tests/formats/text_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { textFormatter } from '../../src/formats/text.js'; import { GenerateResponseChunk } from '../../src/generate.js'; diff --git a/js/ai/tests/generate/chunk_test.ts b/js/ai/tests/generate/chunk_test.ts index 21c5d6718..40a244867 100644 --- a/js/ai/tests/generate/chunk_test.ts +++ b/js/ai/tests/generate/chunk_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { GenerateResponseChunk } from '../../src/generate.js'; diff --git a/js/ai/tests/generate/generate_test.ts b/js/ai/tests/generate/generate_test.ts index 594375144..6b91ac249 100644 --- a/js/ai/tests/generate/generate_test.ts +++ b/js/ai/tests/generate/generate_test.ts @@ -16,7 +16,7 @@ import { PluginProvider, z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { GenerateOptions, diff --git a/js/ai/tests/generate/response_test.ts b/js/ai/tests/generate/response_test.ts index 6689a3ee1..c35ed210b 100644 --- a/js/ai/tests/generate/response_test.ts +++ b/js/ai/tests/generate/response_test.ts @@ -16,7 +16,7 @@ import { z } from '@genkit-ai/core'; import { toJsonSchema } from '@genkit-ai/core/schema'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { GenerateResponse, diff --git a/js/ai/tests/message/message_test.ts b/js/ai/tests/message/message_test.ts index 8046a9aad..07a85f9ec 100644 --- a/js/ai/tests/message/message_test.ts +++ b/js/ai/tests/message/message_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { Message } from '../../src/message'; diff --git a/js/ai/tests/model/document_test.ts b/js/ai/tests/model/document_test.ts index c1adaa97f..b8f4e30c1 100644 --- a/js/ai/tests/model/document_test.ts +++ b/js/ai/tests/model/document_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { Document } from '../../src/document.js'; diff --git a/js/ai/tests/model/middleware_test.ts b/js/ai/tests/model/middleware_test.ts index ae93d9d44..8b2156d71 100644 --- a/js/ai/tests/model/middleware_test.ts +++ b/js/ai/tests/model/middleware_test.ts @@ -15,7 +15,7 @@ */ import { Registry } from '@genkit-ai/core/registry'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { DocumentData } from '../../src/document.js'; import { configureFormats } from '../../src/formats/index.js'; @@ -90,7 +90,7 @@ describe('validateSupport', () => { for (const example of Object.values(examples)) { runner(example, noopNext); } - assert(nextCalled, "next() wasn't called"); + assert.ok(nextCalled, "next() wasn't called"); }); it('throws when media is supplied but not supported', async () => { diff --git a/js/ai/tests/prompt/prompt_test.ts b/js/ai/tests/prompt/prompt_test.ts index 702f85444..9c2d40d8d 100644 --- a/js/ai/tests/prompt/prompt_test.ts +++ b/js/ai/tests/prompt/prompt_test.ts @@ -16,7 +16,7 @@ import { z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { definePrompt, renderPrompt } from '../../src/prompt.ts'; diff --git a/js/ai/tests/reranker/reranker_test.ts b/js/ai/tests/reranker/reranker_test.ts index 63a8b25e4..1c5020cc0 100644 --- a/js/ai/tests/reranker/reranker_test.ts +++ b/js/ai/tests/reranker/reranker_test.ts @@ -16,7 +16,7 @@ import { GenkitError, z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { defineReranker, rerank } from '../../src/reranker'; import { Document } from '../../src/retriever'; @@ -70,8 +70,8 @@ describe('reranker', () => { }); // Validate the reranked results assert.equal(rerankedDocuments.length, 2); - assert(rerankedDocuments[0].text.includes('a bit longer')); - assert(rerankedDocuments[1].text.includes('short')); + assert.ok(rerankedDocuments[0].text.includes('a bit longer')); + assert.ok(rerankedDocuments[1].text.includes('short')); }); it('handles missing options gracefully', async () => { @@ -109,7 +109,7 @@ describe('reranker', () => { options: { k: 2 }, }); assert.equal(rerankedDocuments.length, 2); - assert(typeof rerankedDocuments[0].metadata.score === 'number'); + assert.ok(typeof rerankedDocuments[0].metadata.score === 'number'); }); it('validates config schema and throws error on invalid input', async () => { @@ -147,7 +147,7 @@ describe('reranker', () => { }); assert.fail('Expected validation error'); } catch (err) { - assert(err instanceof GenkitError); + assert.ok(err instanceof GenkitError); assert.equal(err.status, 'INVALID_ARGUMENT'); } }); @@ -211,7 +211,7 @@ describe('reranker', () => { }); assert.fail('Expected an error to be thrown'); } catch (err) { - assert(err instanceof GenkitError); + assert.ok(err instanceof GenkitError); assert.equal(err.status, 'INTERNAL'); assert.equal( err.message, diff --git a/js/core/src/reflection.ts b/js/core/src/reflection.ts index d8d2c4fba..a4cc79298 100644 --- a/js/core/src/reflection.ts +++ b/js/core/src/reflection.ts @@ -19,7 +19,7 @@ import fs from 'fs/promises'; import getPort, { makeRange } from 'get-port'; import { Server } from 'http'; import path from 'path'; -import z from 'zod'; +import * as z from 'zod'; import { Status, StatusCodes, runWithStreamingCallback } from './action.js'; import { GENKIT_REFLECTION_API_SPEC_VERSION, GENKIT_VERSION } from './index.js'; import { logger } from './logging.js'; diff --git a/js/core/tests/action_test.ts b/js/core/tests/action_test.ts index 473718616..0b4391b15 100644 --- a/js/core/tests/action_test.ts +++ b/js/core/tests/action_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { z } from 'zod'; import { action, defineAction } from '../src/action.js'; diff --git a/js/core/tests/flow_test.ts b/js/core/tests/flow_test.ts index deb78364e..a4b0f76f0 100644 --- a/js/core/tests/flow_test.ts +++ b/js/core/tests/flow_test.ts @@ -15,7 +15,7 @@ */ import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { defineFlow, run } from '../src/flow.js'; import { defineAction, getFlowAuth, z } from '../src/index.js'; diff --git a/js/core/tests/registry_test.ts b/js/core/tests/registry_test.ts index 2b52dde0b..86d7a2861 100644 --- a/js/core/tests/registry_test.ts +++ b/js/core/tests/registry_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { action } from '../src/action.js'; import { Registry } from '../src/registry.js'; diff --git a/js/core/tests/schema_test.ts b/js/core/tests/schema_test.ts index 4b3152d2a..faba9627b 100644 --- a/js/core/tests/schema_test.ts +++ b/js/core/tests/schema_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { diff --git a/js/genkit/tests/chat_test.ts b/js/genkit/tests/chat_test.ts index 1d6c148de..5323b335b 100644 --- a/js/genkit/tests/chat_test.ts +++ b/js/genkit/tests/chat_test.ts @@ -16,7 +16,7 @@ import { MessageData } from '@genkit-ai/ai'; import { z } from '@genkit-ai/core'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { Genkit, genkit } from '../src/genkit'; import { diff --git a/js/genkit/tests/embed_test.ts b/js/genkit/tests/embed_test.ts index 18d940dde..c93c6ca51 100644 --- a/js/genkit/tests/embed_test.ts +++ b/js/genkit/tests/embed_test.ts @@ -15,7 +15,7 @@ */ import { Document, EmbedderAction, embedderRef } from '@genkit-ai/ai'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { Genkit, genkit } from '../src/genkit'; diff --git a/js/genkit/tests/evaluate_test.ts b/js/genkit/tests/evaluate_test.ts index f99b88365..fdd33d400 100644 --- a/js/genkit/tests/evaluate_test.ts +++ b/js/genkit/tests/evaluate_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { Genkit, genkit } from '../src/genkit'; import { bonknessEvaluator } from './helpers'; diff --git a/js/genkit/tests/flow_test.ts b/js/genkit/tests/flow_test.ts index 714b5b111..951a7af2f 100644 --- a/js/genkit/tests/flow_test.ts +++ b/js/genkit/tests/flow_test.ts @@ -15,7 +15,7 @@ */ import { z } from '@genkit-ai/core'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { Genkit, genkit } from '../src/genkit'; diff --git a/js/genkit/tests/formats_test.ts b/js/genkit/tests/formats_test.ts index 1a1c5f242..8613b1553 100644 --- a/js/genkit/tests/formats_test.ts +++ b/js/genkit/tests/formats_test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { Genkit, genkit } from '../src/genkit'; import { defineEchoModel } from './helpers'; diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index 75a954898..504040ea2 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -15,7 +15,7 @@ */ import { z } from '@genkit-ai/core'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { modelRef } from '../../ai/src/model'; import { Genkit, genkit } from '../src/genkit'; diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index bc6a2a240..a85283eac 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -15,7 +15,7 @@ */ import { ModelMiddleware, modelRef } from '@genkit-ai/ai/model'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { Genkit, genkit } from '../src/genkit'; import { PromptAction, z } from '../src/index'; diff --git a/js/genkit/tests/session_test.ts b/js/genkit/tests/session_test.ts index d49e01fc5..8bdebe355 100644 --- a/js/genkit/tests/session_test.ts +++ b/js/genkit/tests/session_test.ts @@ -16,7 +16,7 @@ import { Message } from '@genkit-ai/ai'; import { SessionStore } from '@genkit-ai/ai/session'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { Genkit, genkit } from '../src/genkit'; import { TestMemorySessionStore, defineEchoModel } from './helpers'; diff --git a/js/plugins/dotprompt/src/template.ts b/js/plugins/dotprompt/src/template.ts index 04f984eea..3f0b89290 100644 --- a/js/plugins/dotprompt/src/template.ts +++ b/js/plugins/dotprompt/src/template.ts @@ -16,7 +16,7 @@ import { MediaPart, MessageData, Part, Role } from '@genkit-ai/ai/model'; import { DocumentData } from '@genkit-ai/ai/retriever'; -import Handlebars from 'handlebars'; +import * as Handlebars from 'handlebars'; import { PromptMetadata } from './metadata.js'; const Promptbars: typeof Handlebars = diff --git a/js/plugins/dotprompt/tests/picoschema_test.ts b/js/plugins/dotprompt/tests/picoschema_test.ts index eccf025e6..d341e3b8f 100644 --- a/js/plugins/dotprompt/tests/picoschema_test.ts +++ b/js/plugins/dotprompt/tests/picoschema_test.ts @@ -14,8 +14,8 @@ * limitations under the License. */ +import * as assert from 'assert'; import { readFileSync } from 'fs'; -import assert from 'node:assert'; import { describe, it } from 'node:test'; import { parse } from 'yaml'; import { picoschema } from '../src/picoschema'; diff --git a/js/plugins/dotprompt/tests/prompt_test.ts b/js/plugins/dotprompt/tests/prompt_test.ts index 52da58f69..86c959dc6 100644 --- a/js/plugins/dotprompt/tests/prompt_test.ts +++ b/js/plugins/dotprompt/tests/prompt_test.ts @@ -23,7 +23,7 @@ import { toJsonSchema, ValidationError, } from '@genkit-ai/core/schema'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { defineDotprompt, Dotprompt, prompt, promptRef } from '../src/index.js'; import { PromptMetadata } from '../src/metadata.js'; diff --git a/js/plugins/dotprompt/tests/template_test.ts b/js/plugins/dotprompt/tests/template_test.ts index 217f0e405..5df3791af 100644 --- a/js/plugins/dotprompt/tests/template_test.ts +++ b/js/plugins/dotprompt/tests/template_test.ts @@ -16,7 +16,7 @@ import { MessageData } from '@genkit-ai/ai/model'; import { DocumentData } from '@genkit-ai/ai/retriever'; -import assert from 'node:assert'; +import * as assert from 'assert'; import { describe, it } from 'node:test'; import { compile } from '../src/template'; diff --git a/js/plugins/express/tests/express_test.ts b/js/plugins/express/tests/express_test.ts index 77d9bc85b..ad6a9a958 100644 --- a/js/plugins/express/tests/express_test.ts +++ b/js/plugins/express/tests/express_test.ts @@ -14,13 +14,13 @@ * limitations under the License. */ +import * as assert from 'assert'; import express from 'express'; import { GenerateResponseData, Genkit, genkit, z } from 'genkit'; import { runFlow, streamFlow } from 'genkit/client'; import { GenerateResponseChunkData, ModelAction } from 'genkit/model'; import getPort from 'get-port'; import * as http from 'http'; -import assert from 'node:assert'; import { afterEach, beforeEach, describe, it } from 'node:test'; import { FlowServer, diff --git a/js/plugins/firebase/tests/functions_test.ts b/js/plugins/firebase/tests/functions_test.ts index e70cf10b7..365daec3a 100644 --- a/js/plugins/firebase/tests/functions_test.ts +++ b/js/plugins/firebase/tests/functions_test.ts @@ -14,12 +14,12 @@ * limitations under the License. */ import { afterEach, beforeEach, describe, expect, it } from '@jest/globals'; -import express from 'express'; +import * as express from 'express'; import { initializeApp } from 'firebase/app'; import { getFunctions, httpsCallableFromURL } from 'firebase/functions'; import { Genkit, genkit } from 'genkit'; import { runFlow, streamFlow } from 'genkit/client'; -import getPort from 'get-port'; +import * as getPort from 'get-port'; import * as http from 'http'; import { RequestWithAuth, noAuth, onFlow } from '../lib/functions.js'; diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index 292eec051..e2e1a3d39 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -21,7 +21,7 @@ "build:clean": "rimraf ./lib", "build": "npm-run-all build:clean check compile", "build:watch": "tsup-node --watch", - "test": "node --experimental-vm-modules node_modules/jest/bin/jest --runInBand --verbose" + "test": "node node_modules/jest/bin/jest --runInBand --verbose" }, "repository": { "type": "git", diff --git a/js/plugins/google-cloud/tests/logs_no_io_test.ts b/js/plugins/google-cloud/tests/logs_no_io_test.ts index ec00cdfec..71a26ae2e 100644 --- a/js/plugins/google-cloud/tests/logs_no_io_test.ts +++ b/js/plugins/google-cloud/tests/logs_no_io_test.ts @@ -23,8 +23,8 @@ import { jest, } from '@jest/globals'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import * as assert from 'assert'; import { GenerateResponseData, Genkit, genkit, z } from 'genkit'; -import assert from 'node:assert'; import { Writable } from 'stream'; import { __addTransportStreamForTesting, diff --git a/js/plugins/google-cloud/tests/logs_session_test.ts b/js/plugins/google-cloud/tests/logs_session_test.ts index 53552a971..9113a3c96 100644 --- a/js/plugins/google-cloud/tests/logs_session_test.ts +++ b/js/plugins/google-cloud/tests/logs_session_test.ts @@ -24,9 +24,9 @@ import { jest, } from '@jest/globals'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import * as assert from 'assert'; import { GenerateResponseData, Genkit, genkit, z } from 'genkit'; import { ModelAction } from 'genkit/model'; -import assert from 'node:assert'; import { Writable } from 'stream'; import { __addTransportStreamForTesting, diff --git a/js/plugins/google-cloud/tests/logs_test.ts b/js/plugins/google-cloud/tests/logs_test.ts index b2622fdf8..e4540c5c1 100644 --- a/js/plugins/google-cloud/tests/logs_test.ts +++ b/js/plugins/google-cloud/tests/logs_test.ts @@ -23,9 +23,9 @@ import { jest, } from '@jest/globals'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import * as assert from 'assert'; import { GenerateResponseData, Genkit, genkit, z } from 'genkit'; import { SPAN_TYPE_ATTR, appendSpan } from 'genkit/tracing'; -import assert from 'node:assert'; import { Writable } from 'stream'; import { __addTransportStreamForTesting, diff --git a/js/plugins/google-cloud/tests/metrics_test.ts b/js/plugins/google-cloud/tests/metrics_test.ts index 8bba5fcce..30bca1681 100644 --- a/js/plugins/google-cloud/tests/metrics_test.ts +++ b/js/plugins/google-cloud/tests/metrics_test.ts @@ -30,9 +30,9 @@ import { SumMetricData, } from '@opentelemetry/sdk-metrics'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import * as assert from 'assert'; import { GenerateResponseData, Genkit, genkit, z } from 'genkit'; import { SPAN_TYPE_ATTR, appendSpan } from 'genkit/tracing'; -import assert from 'node:assert'; import { GcpOpenTelemetry, __forceFlushSpansForTesting, diff --git a/js/plugins/google-cloud/tests/traces_test.ts b/js/plugins/google-cloud/tests/traces_test.ts index 47fc9b666..9ed08d53a 100644 --- a/js/plugins/google-cloud/tests/traces_test.ts +++ b/js/plugins/google-cloud/tests/traces_test.ts @@ -24,9 +24,9 @@ import { jest, } from '@jest/globals'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import * as assert from 'assert'; import { Genkit, genkit, z } from 'genkit'; import { appendSpan } from 'genkit/tracing'; -import assert from 'node:assert'; import { __forceFlushSpansForTesting, __getSpanExporterForTesting, diff --git a/js/plugins/googleai/tests/gemini_test.ts b/js/plugins/googleai/tests/gemini_test.ts index 6ae5da39f..0ce663c97 100644 --- a/js/plugins/googleai/tests/gemini_test.ts +++ b/js/plugins/googleai/tests/gemini_test.ts @@ -15,9 +15,9 @@ */ import { GenerateContentCandidate } from '@google/generative-ai'; +import * as assert from 'assert'; import { genkit } from 'genkit'; import { MessageData, ModelInfo } from 'genkit/model'; -import assert from 'node:assert'; import { afterEach, beforeEach, describe, it } from 'node:test'; import { GENERIC_GEMINI_MODEL, diff --git a/js/plugins/ollama/tests/embedding_live_test.ts b/js/plugins/ollama/tests/embedding_live_test.ts index 64bd953b6..658be5408 100644 --- a/js/plugins/ollama/tests/embedding_live_test.ts +++ b/js/plugins/ollama/tests/embedding_live_test.ts @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import * as assert from 'assert'; import { genkit } from 'genkit'; -import assert from 'node:assert'; import { describe, it } from 'node:test'; import { defineOllamaEmbedder } from '../src/embeddings.js'; // Adjust the import path as necessary import { ollama } from '../src/index.js'; diff --git a/js/plugins/ollama/tests/embeddings_test.ts b/js/plugins/ollama/tests/embeddings_test.ts index 7a9b98d6d..d2a20cb44 100644 --- a/js/plugins/ollama/tests/embeddings_test.ts +++ b/js/plugins/ollama/tests/embeddings_test.ts @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import * as assert from 'assert'; import { Genkit, genkit } from 'genkit'; -import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; import { defineOllamaEmbedder } from '../src/embeddings.js'; import { ollama } from '../src/index.js'; @@ -87,7 +87,7 @@ describe('defineOllamaEmbedder', () => { }); }, (error) => { - assert(error instanceof Error); + assert.ok(error instanceof Error); assert.strictEqual( error.message, 'Error fetching embedding from Ollama: Internal Server Error' diff --git a/js/plugins/vertexai/tests/gemini_test.ts b/js/plugins/vertexai/tests/gemini_test.ts index 025b00447..aeb388d37 100644 --- a/js/plugins/vertexai/tests/gemini_test.ts +++ b/js/plugins/vertexai/tests/gemini_test.ts @@ -15,8 +15,8 @@ */ import { GenerateContentCandidate } from '@google-cloud/vertexai'; +import * as assert from 'assert'; import { MessageData } from 'genkit'; -import assert from 'node:assert'; import { describe, it } from 'node:test'; import { cleanSchema, diff --git a/js/plugins/vertexai/tests/modelgarden/anthropic_test.ts b/js/plugins/vertexai/tests/modelgarden/anthropic_test.ts index 61849b8dc..abf0988ec 100644 --- a/js/plugins/vertexai/tests/modelgarden/anthropic_test.ts +++ b/js/plugins/vertexai/tests/modelgarden/anthropic_test.ts @@ -18,8 +18,8 @@ import { Message, MessageCreateParamsBase, } from '@anthropic-ai/sdk/resources/messages.mjs'; +import * as assert from 'assert'; import { GenerateRequest, GenerateResponseData } from 'genkit'; -import assert from 'node:assert'; import { describe, it } from 'node:test'; import { AnthropicConfigSchema, diff --git a/js/plugins/vertexai/tests/modelgarden/mistral_test.ts b/js/plugins/vertexai/tests/modelgarden/mistral_test.ts index e7b5efef2..7319bc8d4 100644 --- a/js/plugins/vertexai/tests/modelgarden/mistral_test.ts +++ b/js/plugins/vertexai/tests/modelgarden/mistral_test.ts @@ -19,8 +19,8 @@ import { ChatCompletionResponse, CompletionChunk, } from '@mistralai/mistralai-gcp/models/components'; +import * as assert from 'assert'; import { GenerateRequest, GenerateResponseData } from 'genkit'; -import assert from 'node:assert'; import { describe, it } from 'node:test'; import { MistralConfigSchema, diff --git a/js/plugins/vertexai/tests/vectorsearch/bigquery_test.ts b/js/plugins/vertexai/tests/vectorsearch/bigquery_test.ts index cf94ed8de..f39021b25 100644 --- a/js/plugins/vertexai/tests/vectorsearch/bigquery_test.ts +++ b/js/plugins/vertexai/tests/vectorsearch/bigquery_test.ts @@ -15,8 +15,8 @@ */ import { BigQuery } from '@google-cloud/bigquery'; +import * as assert from 'assert'; import { Document } from 'genkit/retriever'; -import assert from 'node:assert'; import { describe, it } from 'node:test'; import { getBigQueryDocumentRetriever } from '../../src/vectorsearch'; diff --git a/js/tsconfig.json b/js/tsconfig.json index 521b487b7..5ec0327e6 100644 --- a/js/tsconfig.json +++ b/js/tsconfig.json @@ -1,5 +1,8 @@ { "compilerOptions": { + // Please do not turn on helper settings like esModuleInterop because + // customers either need to use the same helper settings or turn on + // skipLibCheck. "declaration": true, "module": "NodeNext", "moduleResolution": "NodeNext", @@ -9,7 +12,7 @@ "noImplicitAny": false, "resolveJsonModule": true, "sourceMap": true, - "esModuleInterop": true, + "esModuleInterop": false, "strict": true, "target": "es2022", "lib": ["es2022", "DOM"], From ce58ea0e8822350d2091c084aa0ddd486011eb0f Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 22 Jan 2025 07:25:15 -0800 Subject: [PATCH 214/562] Add /ai/formats to typesVersions to fix some users' builds (#1636) --- js/ai/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js/ai/package.json b/js/ai/package.json index bb413c94c..1336bc593 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -154,6 +154,9 @@ ], "session": [ "lib/session" + ], + "formats": [ + "lib/formats" ] } } From cbebac004f2ec7de13a138527a320ca05aeae339 Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Wed, 22 Jan 2025 11:11:10 -0500 Subject: [PATCH 215/562] move lib options down in scope (#1637) --- js/genkit/tsconfig.json | 5 ++++- js/tsconfig.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/js/genkit/tsconfig.json b/js/genkit/tsconfig.json index 3cf966d7c..a69ba803f 100644 --- a/js/genkit/tsconfig.json +++ b/js/genkit/tsconfig.json @@ -1,4 +1,7 @@ { "extends": "../tsconfig.json", - "include": ["src"] + "include": ["src"], + "compilerOptions": { + "lib": ["es2022", "DOM"] + } } diff --git a/js/tsconfig.json b/js/tsconfig.json index 5ec0327e6..8198b8d7b 100644 --- a/js/tsconfig.json +++ b/js/tsconfig.json @@ -15,7 +15,7 @@ "esModuleInterop": false, "strict": true, "target": "es2022", - "lib": ["es2022", "DOM"], + "lib": ["es2022"], "noEmit": true }, "compileOnSave": true From 7f38d332e7320052df47118a3714a67c6a5b39d9 Mon Sep 17 00:00:00 2001 From: thedmail Date: Wed, 22 Jan 2025 13:32:18 -0800 Subject: [PATCH 216/562] Update plugin-authoring-evaluator.md (#1646) Fixes rendering of {{macro}} and numbered list at bottom of page. --- docs/plugin-authoring-evaluator.md | 48 ++++++++++++++++-------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/docs/plugin-authoring-evaluator.md b/docs/plugin-authoring-evaluator.md index 031169fff..6b8c58172 100644 --- a/docs/plugin-authoring-evaluator.md +++ b/docs/plugin-authoring-evaluator.md @@ -1,7 +1,7 @@ # Writing a Genkit Evaluator -You can extend Firebase Genkit to support custom evaluation, using either an -LLM as a judge, or by programmatic (heuristic) evaluation. +You can extend Firebase Genkit to support custom evaluation, using either +an LLM as a judge, or by programmatic (heuristic) evaluation. ## Evaluator definition @@ -74,10 +74,7 @@ function getDeliciousnessPrompt(ai: Genkit) { Output: A juicy piece of gossip Response: { "reason": "Metaphorically 'tasty' but not food.", "verdict": "maybe" } - New Output: - {% verbatim %} - {{ responseToTest }} - {% endverbatim %} + New Output: {% verbatim %}{{ responseToTest }} {% endverbatim %} Response: ` ); @@ -87,7 +84,8 @@ function getDeliciousnessPrompt(ai: Genkit) { #### Define the scoring function Define a function that takes an example that includes `output` as -required by the prompt, and scores the result. Genkit testcases include `input` as +required by the prompt, and scores the result. Genkit testcases include +`input` as a required field, with `output` and `context` as optional fields. It is the responsibility of the evaluator to validate that all fields required for evaluation are present. @@ -226,8 +224,8 @@ The `defineEvaluator` object lets the user provide a name, a user-readable display name, and a definition for the evaluator. The display name and definiton are displayed along with evaluation results in the Dev UI. It also has an optional `isBilled` field that marks whether this evaluator -can result in billing (e.g., it uses a billed LLM or API). If an evaluator is -billed, the UI prompts the user for a confirmation in the CLI before +can result in billing (e.g., it uses a billed LLM or API). If an evaluator +is billed, the UI prompts the user for a confirmation in the CLI before allowing them to run an evaluation. This step helps guard against unintended expenses. @@ -311,7 +309,7 @@ initializing Genkit. To define a new plugin, use the `genkitPlugin` helper method to instantiate all Genkit actions within the plugin context. This code sample shows two evaluators: the LLM-based deliciousness evaluator, -and the regex-based US phone number evaluator. Instatiating these +and the regex-based US phone number evaluator. Instantiating these evaluators within the plugin context registers them with the plugin. ```ts @@ -361,12 +359,15 @@ const ai = genkit({ ## Using your custom evaluators -Once you instantiate your custom evaluators within the Genkit app context (either -through a plugin or directly), they are ready to be used. The following example -illustrates how to try out the deliciousness evaluator with a few sample -inputs and outputs. +Once you instantiate your custom evaluators within the Genkit app context +(either through a plugin or directly), they are ready to be used. The following +example illustrates how to try out the deliciousness evaluator with a few sample +inputs and outputs. -1. Create a json file `deliciousness_dataset.json` with the following content: +
    +
  • 1. Create a json file `deliciousness_dataset.json` with the following + content:
  • +
```json [ @@ -382,8 +383,9 @@ inputs and outputs. } ] ``` - -2. Use the Genkit CLI to run the evaluator against these test cases. +
    +
  • 2. Use the Genkit CLI to run the evaluator against these test cases.
  • +
```posix-terminal # Start your genkit runtime @@ -391,10 +393,12 @@ genkit start -- genkit eval:run deliciousness_dataset.json --evaluators=myCustomEvals/deliciousnessEvaluator ``` - -Navigate to `localhost:4000/evaluate` to view your results in the Genkit UI. +
    +
  • 3. Navigate to `localhost:4000/evaluate` to view your results in the + Genkit UI.
  • +
It is important to note that confidence in custom evaluators increases as -you benchmark them with standard datasets or approaches. Iterate on the results -of such benchmarks to improve your evaluators' performance until it reaches the -desired quality. +you benchmark them with standard datasets or approaches. Iterate on the +results of such benchmarks to improve your evaluators' performance until it +reaches the targeted level of quality. From 34b559ea03bda033533ee5cfc844a49fa2cbdb6e Mon Sep 17 00:00:00 2001 From: Ali Ghaznavi <55113970+mghaznav@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:43:07 -0500 Subject: [PATCH 217/562] Clarify Firebase plugin documentation (#1645) * Clarified Genkit Monitoring setup instructions Provided more clarity on how to set up Genkit Monitoring in regards to required permissions for Google's Cloud operations. --- docs/plugins/firebase.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/plugins/firebase.md b/docs/plugins/firebase.md index 6aa4967b5..23cf74ce2 100644 --- a/docs/plugins/firebase.md +++ b/docs/plugins/firebase.md @@ -8,7 +8,7 @@ The Firebase plugin provides integrations with Firebase services, allowing you t - **Firestore Vector Store**: Use Firestore for indexing and retrieval with vector embeddings. - **Cloud Functions**: Deploy flows as HTTPS-triggered functions. - **Firebase Authentication**: Implement authorization policies. -- **Telemetry**: Export telemetry to [Google Cloud’s operations suite](https://cloud.google.com/products/operations) and see specialized views in the Firebase console +- **Telemetry**: Export telemetry to [Google's Cloud operations suite](https://cloud.google.com/products/operations) that powers the Firebase Genkit Monitoring console. ## Installation @@ -63,7 +63,15 @@ To provide Firebase credentials, you also need to set up Google Cloud Applicatio ### Telemetry -To enable telemetry export to Google Cloud's operations suite call `enableFirebaseTelemetry()`: +Firebase Genkit Monitoring is powered by Google's Cloud operation suite. This requires telemetry related API's to be enabled for your project. Please refer to the [Google Cloud plugin](google-cloud.md#set-up-a-google-cloud-account) documentation for more details. + +Grant the following roles to the **"Default compute service account"** within the [Google Cloud IAM Console](https://pantheon.corp.google.com/iam-admin/iam?authuser=0): + +- **Monitoring Metric Writer** (roles/monitoring.metricWriter) +- **Cloud Trace Agent** (roles/cloudtrace.agent) +- **Logs Writer** (roles/logging.logWriter) + +To enable telemetry export call `enableFirebaseTelemetry()`: From 98ce510632a5904349efe208df210936dcbcc1e4 Mon Sep 17 00:00:00 2001 From: Ali Ghaznavi <55113970+mghaznav@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:52:40 -0500 Subject: [PATCH 218/562] Make observability docs, Firebase Genkit monitoring centric. (#1648) --- docs/monitoring.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/monitoring.md b/docs/monitoring.md index 68770daee..6a8f28dc5 100644 --- a/docs/monitoring.md +++ b/docs/monitoring.md @@ -1,11 +1,15 @@ # Monitoring -Firebase Genkit is fully instrumented with +Firebase Genkit has a robust set of observability and monitoring features. + +Genkit is fully instrumented with [OpenTelemetry](https://opentelemetry.io/) and provides built-in telemetry support for tracing and metrics. -## Telemetry Configuration +The [Genkit Monitoring dashboard](https://console.firebase.google.com/project/_/genai_monitoring) helps you with understanding the overall health of your features, and for debugging stability as well as content quality issues that may point to problems with your LLM prompts and Genkit Flows. + +## Telemetry -Genkit automatically manages tracing and metrics without requiring explicit configuration. You can enable telemetry exports to Firebase using the respective plugin and helper function. Using the Firebase plugin powers the [Genkit Monitoring dashboard](https://console.firebase.google.com/project/_/genai_monitoring) that has an AI-centric view of telemetry data. + You can enable telemetry exports to the Genkit Monitoring dashboard using the Firebase plugin and helper function. ```ts import { genkit } from 'genkit'; @@ -21,6 +25,8 @@ const ai = genkit({ ``` More details are outlined in the [Firebase plugin docs](./plugins/firebase.md). +Note: Genkit automatically manages tracing and metrics without requiring explicit configuration. + ## Logging Genkit provides a centralized logging system that can be configured using the logging module. Logs will be exported Google Cloud operations suite if telemetry export is enabled. From 2354f1700851276a43245662decf6be2434b8b20 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:19:39 +0000 Subject: [PATCH 219/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.4 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 6f7724e40..7907e1a0d 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 4d9ae8b79e004d8e96625ee64c5edb6991aa866e Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:19:42 +0000 Subject: [PATCH 220/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.4 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 5e7837b93..0644d21a4 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From 7739636a89fce2ad6354d3ece353d752cf6aba4a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:19:44 +0000 Subject: [PATCH 221/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.4 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index a8dc3b4e9..1166bfae3 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From d136d1f6c6d0cbd5a443dc58316539a2e0f2284e Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:07 +0000 Subject: [PATCH 222/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.4 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 883066cfa..d85156f5c 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From f4b4254c87e3ea896133cd7ac23ac44da13b19a9 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:10 +0000 Subject: [PATCH 223/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.4 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index 1336bc593..9c4a301e4 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From 1f875681fc8a32ae099870e1e0b5d56c21384ca1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:12 +0000 Subject: [PATCH 224/562] chore: bump genkit version to genkit@1.0.0-rc.4 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index 965850584..c4f408370 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From e7b3fbf2ec0faa1121d55d360dbbff1ff7e907b1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:15 +0000 Subject: [PATCH 225/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.4 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index e7ff64f76..55ebf3e72 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From 1767912ba3ddf967c5df572d9bb449197f684a98 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:18 +0000 Subject: [PATCH 226/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.4 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 2ee53980c..9e97b3266 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From d8ab486e5688b3eece891a33a64267af0c3ec0eb Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:21 +0000 Subject: [PATCH 227/562] chore: bump @genkit-ai/dotprompt version to @genkit-ai/dotprompt@1.0.0-rc.4 --- js/plugins/dotprompt/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dotprompt/package.json b/js/plugins/dotprompt/package.json index dc7356d2d..28d818330 100644 --- a/js/plugins/dotprompt/package.json +++ b/js/plugins/dotprompt/package.json @@ -9,7 +9,7 @@ "prompting", "templating" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From 2d1b9608d27d09c0aee4ef93d9739068d5044056 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:24 +0000 Subject: [PATCH 228/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.4 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index fff8c025e..306eab32a 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From ee4c63c8d39cb93144f4c0a998689e64831e67c8 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:27 +0000 Subject: [PATCH 229/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.4 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index f0974b423..a2a0d4934 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From 47660f780d417005ff7fa6e97f10b9c881d8a2b0 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:30 +0000 Subject: [PATCH 230/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.4 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index e2e1a3d39..68137e790 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From 9a9bc91225bffbdee0c703a0174085cd2d52955c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:32 +0000 Subject: [PATCH 231/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.4 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 765868fc8..ec0976a0f 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From 80cc3c9df179183f3372e21d89cb4035623865a1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:35 +0000 Subject: [PATCH 232/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.4 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index 4c2abdeec..58c1f9ff8 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From f403390a25a009218b3955a082d5a8080ed4693e Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:38 +0000 Subject: [PATCH 233/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.4 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index b3fd1cecb..9cbae167b 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From 81a59ddec3a82a9a34c23c5d9e1b24f54c27e553 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:41 +0000 Subject: [PATCH 234/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.4 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index bb1826bf4..c186dc076 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From e2f5bd3fa203324195308284158090afa56160ee Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:43 +0000 Subject: [PATCH 235/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.4 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index cb48d911e..73fc7e6d9 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From a77781d60e54654e17b8a3d05032bf86f9087d54 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:46 +0000 Subject: [PATCH 236/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.4 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index 6aa105790..f9e71b15d 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From 9a5be78d3017aced00ec7bd3518029d0b7dee0f4 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:49 +0000 Subject: [PATCH 237/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.4 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index c4cc6c1ff..d4917f653 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From 0a242b90188444e3ae80f691de19dd3e4d74e9df Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 22 Jan 2025 22:21:52 +0000 Subject: [PATCH 238/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.4 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index d031b20c4..27e051833 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.3", + "version": "1.0.0-rc.4", "type": "commonjs", "scripts": { "check": "tsc", From d88951fd166912ee6a3443b5100e2c7f3bce9924 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 23 Jan 2025 13:31:15 -0500 Subject: [PATCH 239/562] feat(js/ai): added toolChoice option to generate requests (including chat and prompt) (#1571) --- genkit-tools/common/src/types/model.ts | 1 + genkit-tools/genkit-schema.json | 11 ++ js/ai/src/chat.ts | 2 + js/ai/src/generate.ts | 15 ++- js/ai/src/generate/action.ts | 130 ++++++++++++++-------- js/ai/src/model.ts | 3 + js/ai/src/prompt.ts | 5 +- js/genkit/src/genkit.ts | 14 +++ js/genkit/tests/chat_test.ts | 21 ++-- js/genkit/tests/generate_test.ts | 23 ++++ js/plugins/dotprompt/src/metadata.ts | 3 + js/plugins/dotprompt/src/prompt.ts | 5 + js/plugins/dotprompt/tests/prompt_test.ts | 19 ++++ js/plugins/googleai/src/gemini.ts | 54 ++++++--- js/plugins/vertexai/src/gemini.ts | 52 ++++++--- js/testapps/flow-simple-ai/src/index.ts | 56 ++++++++++ 16 files changed, 331 insertions(+), 83 deletions(-) diff --git a/genkit-tools/common/src/types/model.ts b/genkit-tools/common/src/types/model.ts index d3f218ff4..b3a34ecf7 100644 --- a/genkit-tools/common/src/types/model.ts +++ b/genkit-tools/common/src/types/model.ts @@ -162,6 +162,7 @@ export const ModelRequestSchema = z.object({ messages: z.array(MessageSchema), config: z.any().optional(), tools: z.array(ToolDefinitionSchema).optional(), + toolChoice: z.enum(['auto', 'required', 'none']).optional(), output: OutputConfigSchema.optional(), context: z.array(DocumentDataSchema).optional(), }); diff --git a/genkit-tools/genkit-schema.json b/genkit-tools/genkit-schema.json index d6f36262f..97ab1caf0 100644 --- a/genkit-tools/genkit-schema.json +++ b/genkit-tools/genkit-schema.json @@ -357,6 +357,14 @@ "$ref": "#/$defs/ToolDefinition" } }, + "toolChoice": { + "type": "string", + "enum": [ + "auto", + "required", + "none" + ] + }, "output": { "type": "object", "properties": { @@ -698,6 +706,9 @@ "tools": { "$ref": "#/$defs/GenerateRequest/properties/tools" }, + "toolChoice": { + "$ref": "#/$defs/GenerateRequest/properties/toolChoice" + }, "output": { "$ref": "#/$defs/GenerateRequest/properties/output" }, diff --git a/js/ai/src/chat.ts b/js/ai/src/chat.ts index c02f77ffb..99247cb18 100644 --- a/js/ai/src/chat.ts +++ b/js/ai/src/chat.ts @@ -188,6 +188,7 @@ export class Chat { ...(await this.requestBase), // these things may get changed by tools calling within generate. tools: response?.request?.tools, + toolChoice: response?.request?.toolChoice, config: response?.request?.config, }); await this.updateMessages(response.messages); @@ -252,6 +253,7 @@ export class Chat { ...(await this.requestBase), // these things may get changed by tools calling within generate. tools: resolvedResponse?.request?.tools, + toolChoice: resolvedResponse?.request?.toolChoice, config: resolvedResponse?.request?.config, }); this.updateMessages(resolvedResponse.messages); diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index ff5c4c595..7443fcec3 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -48,6 +48,9 @@ import { ExecutablePrompt } from './prompt.js'; import { ToolArgument, resolveTools, toToolDefinition } from './tool.js'; export { GenerateResponse, GenerateResponseChunk }; +/** Specifies how tools should be called by the model. */ +export type ToolChoice = 'auto' | 'required' | 'none'; + export interface OutputOptions { format?: string; contentType?: string; @@ -72,6 +75,8 @@ export interface GenerateOptions< messages?: (MessageData & { content: Part[] | string | (string | Part)[] })[]; /** List of registered tool names or actions to treat as a tool for this generation if supported by the underlying model. */ tools?: ToolArgument[]; + /** Specifies how tools should be called by the model. */ + toolChoice?: ToolChoice; /** Configuration for the generation request. */ config?: z.infer; /** Configuration for the desired output of the request. Defaults to the model's default output if unspecified. */ @@ -275,6 +280,7 @@ export async function generate< docs: resolvedOptions.docs, messages: injectInstructions(messages, instructions), tools, + toolChoice: resolvedOptions.toolChoice, config: { version: resolvedModel.version, ...stripUndefinedOptions(resolvedModel.config), @@ -292,11 +298,10 @@ export async function generate< registry, stripNoop(resolvedOptions.onChunk ?? resolvedOptions.streamingCallback), async () => { - const response = await generateHelper( - registry, - params, - resolvedOptions.use - ); + const response = await generateHelper(registry, { + rawRequest: params, + middleware: resolvedOptions.use, + }); const request = await toGenerateRequest(registry, { ...resolvedOptions, tools, diff --git a/js/ai/src/generate/action.ts b/js/ai/src/generate/action.ts index 8a31a87bb..99830bff9 100644 --- a/js/ai/src/generate/action.ts +++ b/js/ai/src/generate/action.ts @@ -41,7 +41,10 @@ import { GenerateResponseData, MessageData, MessageSchema, + ModelAction, + ModelInfo, ModelMiddleware, + ModelRequest, Part, Role, ToolDefinitionSchema, @@ -59,6 +62,8 @@ export const GenerateUtilParamSchema = z.object({ messages: z.array(MessageSchema), /** List of registered tool names for this generation if supported by the underlying model. */ tools: z.array(z.union([z.string(), ToolDefinitionSchema])).optional(), + /** Tool calling mode. `auto` lets the model decide whether to use tools, `required` forces the model to choose a tool, and `none` forces the model not to use any tools. Defaults to `auto`. */ + toolChoice: z.enum(['auto', 'required', 'none']).optional(), /** Configuration for the generation request. */ config: z.any().optional(), /** Configuration for the desired output of the request. Defaults to the model's default output if unspecified. */ @@ -81,13 +86,15 @@ export const GenerateUtilParamSchema = z.object({ */ export async function generateHelper( registry: Registry, - input: z.infer, - middleware?: ModelMiddleware[], - currentTurns?: number, - messageIndex?: number + options: { + rawRequest: z.infer; + middleware?: ModelMiddleware[]; + currentTurn?: number; + messageIndex?: number; + } ): Promise { - currentTurns = currentTurns ?? 0; - messageIndex = messageIndex ?? 0; + let currentTurn = options.currentTurn ?? 0; + let messageIndex = options.messageIndex ?? 0; // do tracing return await runInNewSpan( registry, @@ -101,14 +108,13 @@ export async function generateHelper( }, async (metadata) => { metadata.name = 'generate'; - metadata.input = input; - const output = await generate( - registry, - input, - middleware, - currentTurns!, - messageIndex! - ); + metadata.input = options.rawRequest; + const output = await generate(registry, { + rawRequest: options.rawRequest, + middleware: options.middleware, + currentTurn, + messageIndex, + }); metadata.output = JSON.stringify(output); return output; } @@ -117,12 +123,17 @@ export async function generateHelper( async function generate( registry: Registry, - rawRequest: z.infer, - middleware: ModelMiddleware[] | undefined, - currentTurn: number, - messageIndex: number + options: { + rawRequest: z.infer; + middleware: ModelMiddleware[] | undefined; + currentTurn: number; + messageIndex: number; + } ): Promise { - const { modelAction: model } = await resolveModel(registry, rawRequest.model); + const { modelAction: model } = await resolveModel( + registry, + options.rawRequest.model + ); if (model.__action.metadata?.model.stage === 'deprecated') { logger.warn( `${clc.bold(clc.yellow('Warning:'))} ` + @@ -130,9 +141,12 @@ async function generate( ); } - const tools = await resolveTools(registry, rawRequest.tools); + const tools = await resolveTools(registry, options.rawRequest.tools); - const resolvedFormat = await resolveFormat(registry, rawRequest.output); + const resolvedFormat = await resolveFormat( + registry, + options.rawRequest.output + ); // Create a lookup of tool names with namespaces stripped to original names const toolMap = tools.reduce>((acc, tool) => { const name = tool.__action.name; @@ -148,9 +162,10 @@ async function generate( }, {}); const request = await actionToGenerateRequest( - rawRequest, + options.rawRequest, tools, - resolvedFormat + resolvedFormat, + model ); const accumulatedChunks: GenerateResponseChunkData[] = []; @@ -164,7 +179,7 @@ async function generate( if (streamingCallback) { streamingCallback!( new GenerateResponseChunk(chunk, { - index: messageIndex, + index: options.messageIndex, role: 'model', previousChunks: accumulatedChunks, parser: resolvedFormat?.handler(request.output?.schema) @@ -180,12 +195,12 @@ async function generate( index: number, req: z.infer ) => { - if (!middleware || index === middleware.length) { + if (!options.middleware || index === options.middleware.length) { // end of the chain, call the original model action return await model(req); } - const currentMiddleware = middleware[index]; + const currentMiddleware = options.middleware[index]; return currentMiddleware(req, async (modifiedReq) => dispatch(index + 1, modifiedReq || req) ); @@ -203,14 +218,14 @@ async function generate( const message = response.message!; // would have thrown if no message const toolCalls = message.content.filter((part) => !!part.toolRequest); - if (rawRequest.returnToolRequests || toolCalls.length === 0) { + if (options.rawRequest.returnToolRequests || toolCalls.length === 0) { if (toolCalls.length === 0) { response.assertValidSchema(request); } return response.toJSON(); } - const maxIterations = rawRequest.maxTurns ?? 5; - if (currentTurn + 1 > maxIterations) { + const maxIterations = options.rawRequest.maxTurns ?? 5; + if (options.currentTurn + 1 > maxIterations) { throw new GenerationResponseError( response, `Exceeded maximum tool call iterations (${maxIterations})`, @@ -221,7 +236,8 @@ async function generate( const toolResponses: ToolResponsePart[] = []; let messages: MessageData[] = [...request.messages, message]; - let newTools = rawRequest.tools; + let newTools = options.rawRequest.tools; + let newToolChoice = options.rawRequest.toolChoice; for (const part of toolCalls) { if (!part.toolRequest) { throw Error( @@ -247,6 +263,7 @@ async function generate( ...messages.filter((m) => !m?.metadata?.preamble), ]; newTools = newPreamble.tools; + newToolChoice = newPreamble.toolChoice; } else { toolResponses.push({ toolResponse: { @@ -257,9 +274,9 @@ async function generate( }); } } - messageIndex++; + options.messageIndex++; const nextRequest = { - ...rawRequest, + ...options.rawRequest, messages: [ ...messages, { @@ -268,6 +285,7 @@ async function generate( }, ] as MessageData[], tools: newTools, + toolCoice: newToolChoice, }; // stream out the tool responses streamingCallback?.( @@ -276,28 +294,49 @@ async function generate( content: toolResponses, }, { - index: messageIndex, + index: options.messageIndex, role: 'model', previousChunks: accumulatedChunks, parser: resolvedFormat?.handler(request.output?.schema).parseChunk, } ) ); - return await generateHelper( - registry, - nextRequest, - middleware, - currentTurn + 1, - messageIndex + 1 - ); + return await generateHelper(registry, { + rawRequest: nextRequest, + middleware: options.middleware, + currentTurn: options.currentTurn + 1, + messageIndex: options.messageIndex + 1, + }); } async function actionToGenerateRequest( options: z.infer, - resolvedTools?: ToolAction[], - resolvedFormat?: Formatter + resolvedTools: ToolAction[] | undefined, + resolvedFormat: Formatter | undefined, + model: ModelAction ): Promise { - const out = { + const modelInfo = model.__action.metadata?.model as ModelInfo; + if ( + (options.tools?.length ?? 0) > 0 && + modelInfo?.supports && + !modelInfo?.supports?.tools + ) { + logger.warn( + `The model '${model.__action.name}' does not support tools (you set: ${options.tools?.length} tools). ` + + 'The model may not behave the way you expect.' + ); + } + if ( + options.toolChoice && + modelInfo?.supports && + !modelInfo?.supports?.toolChoice + ) { + logger.warn( + `The model '${model.__action.name}' does not support the 'toolChoice' option (you set: ${options.toolChoice}). ` + + 'The model may not behave the way you expect.' + ); + } + const out: ModelRequest = { messages: options.messages, config: options.config, docs: options.docs, @@ -309,7 +348,10 @@ async function actionToGenerateRequest( }), }, }; - if (!out.output.schema) delete out.output.schema; + if (options.toolChoice) { + out.toolChoice = options.toolChoice; + } + if (out.output && !out.output.schema) delete out.output.schema; return out; } diff --git a/js/ai/src/model.ts b/js/ai/src/model.ts index 56bb3fb09..e2241886e 100644 --- a/js/ai/src/model.ts +++ b/js/ai/src/model.ts @@ -204,6 +204,8 @@ export const ModelInfoSchema = z.object({ contentType: z.array(z.string()).optional(), /** Model can natively support document-based context grounding. */ context: z.boolean().optional(), + /** Model supports controlling tool choice, e.g. forced tool calling. */ + toolChoice: z.boolean().optional(), }) .optional(), /** At which stage of development this model is. @@ -287,6 +289,7 @@ export const ModelRequestSchema = z.object({ messages: z.array(MessageSchema), config: z.any().optional(), tools: z.array(ToolDefinitionSchema).optional(), + toolChoice: z.enum(['auto', 'required', 'none']).optional(), output: OutputConfigSchema.optional(), docs: z.array(DocumentDataSchema).optional(), }); diff --git a/js/ai/src/prompt.ts b/js/ai/src/prompt.ts index 2e47d534d..d434e3575 100644 --- a/js/ai/src/prompt.ts +++ b/js/ai/src/prompt.ts @@ -21,6 +21,7 @@ import { GenerateOptions, GenerateResponse, GenerateStreamResponse, + ToolChoice, } from './generate.js'; import { GenerateRequest, @@ -29,7 +30,7 @@ import { ModelArgument, ModelMiddleware, } from './model.js'; -import { ToolAction } from './tool.js'; +import { ToolAction, ToolArgument } from './tool.js'; /** * Prompt implementation function signature. @@ -64,6 +65,8 @@ export interface PromptConfig { inputSchema?: I; inputJsonSchema?: JSONSchema7; metadata?: Record; + tools?: ToolArgument[]; + toolChoice?: ToolChoice; use?: ModelMiddleware[]; } diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index af8f74db1..b2006722c 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -566,6 +566,20 @@ export class Genkit implements HasRegistry { } else if (opt.use) { resultOptions.use = opt.use; } + if ((promptResult as GenerateOptions).tools) { + resultOptions.tools = (promptResult as GenerateOptions).tools; + } else if (p.__config?.tools) { + resultOptions.tools = p.__config?.tools; + } else if (opt.tools) { + resultOptions.tools = opt.tools; + } + if ((promptResult as GenerateOptions).toolChoice) { + resultOptions.toolChoice = (promptResult as GenerateOptions).toolChoice; + } else if (p.__config?.toolChoice) { + resultOptions.toolChoice = p.__config?.toolChoice; + } else if (opt.toolChoice) { + resultOptions.toolChoice = opt.toolChoice; + } delete (resultOptions as any).input; if ((promptResult as GenerateOptions).prompt) { resultOptions.prompt = (promptResult as GenerateOptions).prompt; diff --git a/js/genkit/tests/chat_test.ts b/js/genkit/tests/chat_test.ts index 5323b335b..988b8a21c 100644 --- a/js/genkit/tests/chat_test.ts +++ b/js/genkit/tests/chat_test.ts @@ -190,6 +190,7 @@ describe('preamble', () => { config: { temperature: 1 }, description: 'Agent B description', tools: ['agentA'], + toolChoice: 'required', }, '{{role "system"}} agent b' ); @@ -200,6 +201,7 @@ describe('preamble', () => { config: { temperature: 2 }, description: 'Agent A description', tools: [agentB], + toolChoice: 'required', }, async () => { return { @@ -218,14 +220,16 @@ describe('preamble', () => { return { message: { role: 'model', - content: [{ text: 'hi from agent a' }], + content: [ + { text: `hi from agent a (toolChoice: ${req.toolChoice})` }, + ], }, }; }; const session = ai.chat(agentA); let { text } = await session.send('hi'); - assert.strictEqual(text, 'hi from agent a'); + assert.strictEqual(text, 'hi from agent a (toolChoice: required)'); assert.deepStrictEqual(pm.lastRequest, { config: { temperature: 2, @@ -254,6 +258,7 @@ describe('preamble', () => { }, }, ], + toolChoice: 'required', }); // transfer to agent B... @@ -273,7 +278,7 @@ describe('preamble', () => { ref: 'ref123', }, } - : { text: 'hi from agent b' }, + : { text: `hi from agent b (toolChoice: ${req.toolChoice})` }, ], }, }; @@ -281,7 +286,7 @@ describe('preamble', () => { ({ text } = await session.send('pls transfer to b')); - assert.deepStrictEqual(text, 'hi from agent b'); + assert.deepStrictEqual(text, 'hi from agent b (toolChoice: required)'); assert.deepStrictEqual(pm.lastRequest, { config: { // TODO: figure out if config should be swapped out as well... @@ -299,7 +304,7 @@ describe('preamble', () => { }, { role: 'model', - content: [{ text: 'hi from agent a' }], + content: [{ text: 'hi from agent a (toolChoice: required)' }], }, { role: 'user', @@ -343,6 +348,7 @@ describe('preamble', () => { }, }, ], + toolChoice: 'required', }); // transfer back to to agent A... @@ -387,7 +393,7 @@ describe('preamble', () => { }, { role: 'model', - content: [{ text: 'hi from agent a' }], + content: [{ text: 'hi from agent a (toolChoice: required)' }], }, { role: 'user', @@ -419,7 +425,7 @@ describe('preamble', () => { }, { role: 'model', - content: [{ text: 'hi from agent b' }], + content: [{ text: 'hi from agent b (toolChoice: required)' }], }, { role: 'user', @@ -463,6 +469,7 @@ describe('preamble', () => { }, }, ], + toolChoice: 'required', }); }); diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index 504040ea2..af5c0cb31 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -83,6 +83,29 @@ describe('generate', () => { }); }); + it('calls the default model with tool choice', async () => { + const response = await ai.generate({ + prompt: 'hi', + toolChoice: 'required', + }); + assert.strictEqual(response.text, 'Echo: hi; config: {}'); + assert.deepStrictEqual(response.request, { + config: { + version: undefined, + }, + docs: undefined, + messages: [ + { + role: 'user', + content: [{ text: 'hi' }], + }, + ], + output: {}, + tools: [], + toolChoice: 'required', + }); + }); + it('streams the default model', async () => { const { response, stream } = await ai.generateStream('hi'); diff --git a/js/plugins/dotprompt/src/metadata.ts b/js/plugins/dotprompt/src/metadata.ts index 686d0b211..288e89bee 100644 --- a/js/plugins/dotprompt/src/metadata.ts +++ b/js/plugins/dotprompt/src/metadata.ts @@ -52,6 +52,9 @@ export interface PromptMetadata< /** Names of tools (registered separately) to allow use of in this prompt. */ tools?: ToolArgument[]; + /** Specifies how tools should be called by the model. */ + toolChoice?: 'auto' | 'required' | 'none'; + /** Model configuration. Not all models support all options. */ config?: z.infer; diff --git a/js/plugins/dotprompt/src/prompt.ts b/js/plugins/dotprompt/src/prompt.ts index 83bbeec5c..fe465b8de 100644 --- a/js/plugins/dotprompt/src/prompt.ts +++ b/js/plugins/dotprompt/src/prompt.ts @@ -81,6 +81,7 @@ export class Dotprompt implements PromptMetadata { input?: PromptMetadata['input']; output?: PromptMetadata['output']; tools?: PromptMetadata['tools']; + toolChoice?: PromptMetadata['toolChoice']; config?: PromptMetadata['config']; use?: PromptMetadata['use']; @@ -147,6 +148,7 @@ export class Dotprompt implements PromptMetadata { this.input = options.input || { schema: z.any() }; this.output = options.output; this.tools = options.tools; + this.toolChoice = options.toolChoice; this.config = options.config; this.use = options.use; this.template = template; @@ -266,6 +268,9 @@ export class Dotprompt implements PromptMetadata { if (middleware.length > 0) { res.use = middleware; } + if (options.toolChoice || this.toolChoice) { + res.toolChoice = options.toolChoice ?? this.toolChoice; + } return res; } diff --git a/js/plugins/dotprompt/tests/prompt_test.ts b/js/plugins/dotprompt/tests/prompt_test.ts index 86c959dc6..9cccb0d0a 100644 --- a/js/plugins/dotprompt/tests/prompt_test.ts +++ b/js/plugins/dotprompt/tests/prompt_test.ts @@ -138,11 +138,13 @@ describe('Prompt', () => { input: { name: 'Michael' }, onChunk: streamingCallback, returnToolRequests: true, + toolChoice: 'required', maxTurns: 17, use: middleware, }); assert.strictEqual(rendered.onChunk, streamingCallback); assert.strictEqual(rendered.returnToolRequests, true); + assert.strictEqual(rendered.toolChoice, 'required'); assert.strictEqual(rendered.maxTurns, 17); assert.deepStrictEqual(rendered.use, middleware); }); @@ -505,4 +507,21 @@ describe('DotpromptRef', () => { }, ]); }); + + it('should render system prompt', () => { + const model = defineModel( + registry, + { name: 'echo', supports: { tools: true } }, + async (input) => ({ + message: input.messages[0], + finishReason: 'stop', + }) + ); + const prompt = testPrompt(registry, model, `hi`, { + toolChoice: 'required', + }); + + const rendered = prompt.render({ input: {} }); + assert.deepStrictEqual(rendered.toolChoice, 'required'); + }); }); diff --git a/js/plugins/googleai/src/gemini.ts b/js/plugins/googleai/src/gemini.ts index ebd65adfe..0cc6cee10 100644 --- a/js/plugins/googleai/src/gemini.ts +++ b/js/plugins/googleai/src/gemini.ts @@ -103,6 +103,7 @@ export const gemini10Pro = modelRef({ multiturn: true, media: false, tools: true, + toolChoice: true, systemRole: true, }, }, @@ -117,6 +118,7 @@ export const gemini15Pro = modelRef({ multiturn: true, media: true, tools: true, + toolChoice: true, systemRole: true, }, versions: [ @@ -136,6 +138,7 @@ export const gemini15Flash = modelRef({ multiturn: true, media: true, tools: true, + toolChoice: true, systemRole: true, // @ts-ignore contextCache: true, @@ -157,6 +160,7 @@ export const gemini15Flash8b = modelRef({ multiturn: true, media: true, tools: true, + toolChoice: true, systemRole: true, }, versions: ['gemini-1.5-flash-8b-latest', 'gemini-1.5-flash-8b-001'], @@ -173,6 +177,7 @@ export const gemini20FlashExp = modelRef({ multiturn: true, media: true, tools: true, + toolChoice: true, systemRole: true, }, }, @@ -199,6 +204,7 @@ export const GENERIC_GEMINI_MODEL = modelRef({ multiturn: true, media: true, tools: true, + toolChoice: true, systemRole: true, }, }, @@ -680,19 +686,18 @@ export function defineGoogleAIModel( } let toolConfig: ToolConfig | undefined; - if ( - requestConfig.functionCallingConfig && - // This is a workround for issue: https://github.com/firebase/genkit/issues/1520 - // TODO: remove this when the issue is resolved upstream in the Gemini API - !messages.at(-1)?.content.find((c) => c.toolResponse) - ) { + if (requestConfig.functionCallingConfig) { toolConfig = { functionCallingConfig: { allowedFunctionNames: requestConfig.functionCallingConfig.allowedFunctionNames, - mode: toGeminiFunctionMode( - requestConfig.functionCallingConfig.mode - ), + mode: toFunctionModeEnum(requestConfig.functionCallingConfig.mode), + }, + }; + } else if (request.toolChoice) { + toolConfig = { + functionCallingConfig: { + mode: toGeminiFunctionModeEnum(request.toolChoice), }, }; } @@ -821,13 +826,14 @@ export function defineGoogleAIModel( ); } -function toGeminiFunctionMode( - genkitMode: string | undefined +/** Converts mode from the config, which follows Gemini naming convention. */ +function toFunctionModeEnum( + configEnum: string | undefined ): FunctionCallingMode | undefined { - if (genkitMode === undefined) { + if (configEnum === undefined) { return undefined; } - switch (genkitMode) { + switch (configEnum) { case 'MODE_UNSPECIFIED': { return FunctionCallingMode.MODE_UNSPECIFIED; } @@ -840,6 +846,28 @@ function toGeminiFunctionMode( case 'NONE': { return FunctionCallingMode.NONE; } + default: + throw new Error(`unsupported function calling mode: ${configEnum}`); + } +} + +/** Converts mode from genkit tool choice. */ +function toGeminiFunctionModeEnum( + genkitMode: 'auto' | 'required' | 'none' +): FunctionCallingMode | undefined { + if (genkitMode === undefined) { + return undefined; + } + switch (genkitMode) { + case 'required': { + return FunctionCallingMode.ANY; + } + case 'auto': { + return FunctionCallingMode.AUTO; + } + case 'none': { + return FunctionCallingMode.NONE; + } default: throw new Error(`unsupported function calling mode: ${genkitMode}`); } diff --git a/js/plugins/vertexai/src/gemini.ts b/js/plugins/vertexai/src/gemini.ts index 581001619..d65f00479 100644 --- a/js/plugins/vertexai/src/gemini.ts +++ b/js/plugins/vertexai/src/gemini.ts @@ -210,6 +210,7 @@ export const gemini10Pro = modelRef({ media: false, tools: true, systemRole: true, + toolChoice: true, }, }, configSchema: GeminiConfigSchema, @@ -224,6 +225,7 @@ export const gemini15Pro = modelRef({ multiturn: true, media: true, tools: true, + toolChoice: true, systemRole: true, }, }, @@ -239,6 +241,7 @@ export const gemini15Flash = modelRef({ multiturn: true, media: true, tools: true, + toolChoice: true, systemRole: true, }, }, @@ -254,6 +257,7 @@ export const gemini20FlashExp = modelRef({ multiturn: true, media: true, tools: true, + toolChoice: true, systemRole: true, }, }, @@ -624,19 +628,18 @@ export function defineGeminiModel( : []; let toolConfig: ToolConfig | undefined; - if ( - request?.config?.functionCallingConfig && - // This is a workround for issue: https://github.com/firebase/genkit/issues/1520 - // TODO: remove this when the issue is resolved upstream in the Gemini API - !messages.at(-1)?.content.find((c) => c.toolResponse) - ) { + if (request?.config?.functionCallingConfig) { toolConfig = { functionCallingConfig: { allowedFunctionNames: request.config.functionCallingConfig.allowedFunctionNames, - mode: toGeminiFunctionMode( - request.config.functionCallingConfig.mode - ), + mode: toFunctionModeEnum(request.config.functionCallingConfig.mode), + }, + }; + } else if (request.toolChoice) { + toolConfig = { + functionCallingConfig: { + mode: toGeminiFunctionModeEnum(request.toolChoice), }, }; } @@ -798,13 +801,14 @@ export function defineGeminiModel( ); } -function toGeminiFunctionMode( - genkitMode: string | undefined +/** Converts mode from the config, which follows Gemini naming convention. */ +function toFunctionModeEnum( + enumMode: string | undefined ): FunctionCallingMode | undefined { - if (genkitMode === undefined) { + if (enumMode === undefined) { return undefined; } - switch (genkitMode) { + switch (enumMode) { case 'MODE_UNSPECIFIED': { return FunctionCallingMode.MODE_UNSPECIFIED; } @@ -817,6 +821,28 @@ function toGeminiFunctionMode( case 'NONE': { return FunctionCallingMode.NONE; } + default: + throw new Error(`unsupported function calling mode: ${enumMode}`); + } +} + +/** Converts mode from genkit tool choice. */ +function toGeminiFunctionModeEnum( + genkitMode: 'auto' | 'required' | 'none' +): FunctionCallingMode | undefined { + if (genkitMode === undefined) { + return undefined; + } + switch (genkitMode) { + case 'required': { + return FunctionCallingMode.ANY; + } + case 'auto': { + return FunctionCallingMode.AUTO; + } + case 'none': { + return FunctionCallingMode.NONE; + } default: throw new Error(`unsupported function calling mode: ${genkitMode}`); } diff --git a/js/testapps/flow-simple-ai/src/index.ts b/js/testapps/flow-simple-ai/src/index.ts index e973c0eca..031db5e70 100644 --- a/js/testapps/flow-simple-ai/src/index.ts +++ b/js/testapps/flow-simple-ai/src/index.ts @@ -386,6 +386,19 @@ const jokeSubjectGenerator = ai.defineTool( } ); +const gablorkenTool = ai.defineTool( + { + name: 'gablorkenTool', + inputSchema: z.object({ + value: z.number(), + }), + description: 'can be used to calculate gablorken value', + }, + async (input) => { + return input.value * 3 - 4; + } +); + export const toolCaller = ai.defineFlow( { name: 'toolCaller', @@ -414,6 +427,49 @@ export const toolCaller = ai.defineFlow( } ); +const exitTool = ai.defineTool( + { + name: 'exitTool', + inputSchema: z.object({ + answer: z.number(), + }), + description: 'call this tool when you have the final answer', + }, + async (input) => { + throw new Error(`Answer: ${input.answer}`); + } +); + +export const forcedToolCaller = ai.defineFlow( + { + name: 'forcedToolCaller', + inputSchema: z.number(), + outputSchema: z.string(), + streamSchema: z.any(), + }, + async (input, streamingCallback) => { + if (!streamingCallback) { + throw new Error('this flow only works in streaming mode'); + } + + const { response, stream } = await ai.generateStream({ + model: gemini15Flash, + config: { + temperature: 1, + }, + tools: [gablorkenTool, exitTool], + toolChoice: 'required', + prompt: `what is a gablorken of ${input}`, + }); + + for await (const chunk of stream) { + streamingCallback(chunk); + } + + return (await response).text; + } +); + export const invalidOutput = ai.defineFlow( { name: 'invalidOutput', From dead0b821fdf971b692136616451c5e50370ef31 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 23 Jan 2025 14:50:35 -0500 Subject: [PATCH 240/562] feat(js/ai): added a simple way to interrupt tool execution (#1583) --- genkit-tools/common/src/types/model.ts | 18 ++- genkit-tools/genkit-schema.json | 38 +++---- js/ai/src/generate/action.ts | 100 +++++++++++++---- js/ai/src/index.ts | 1 + js/ai/src/model.ts | 20 +++- js/ai/src/tool.ts | 37 +++++- js/genkit/src/genkit.ts | 4 +- js/genkit/tests/generate_test.ts | 149 +++++++++++++++++++++++++ 8 files changed, 305 insertions(+), 62 deletions(-) diff --git a/genkit-tools/common/src/types/model.ts b/genkit-tools/common/src/types/model.ts index b3a34ecf7..9326e6988 100644 --- a/genkit-tools/common/src/types/model.ts +++ b/genkit-tools/common/src/types/model.ts @@ -203,12 +203,22 @@ export const GenerationUsageSchema = z.object({ }); export type GenerationUsage = z.infer; +/** Model response finish reason enum. */ +export const FinishReasonSchema = z.enum([ + 'stop', + 'length', + 'blocked', + 'interrupted', + 'other', + 'unknown', +]); + /** @deprecated All responses now return a single candidate. Only the first candidate will be used if supplied. */ export const CandidateSchema = z.object({ index: z.number(), message: MessageSchema, usage: GenerationUsageSchema.optional(), - finishReason: z.enum(['stop', 'length', 'blocked', 'other', 'unknown']), + finishReason: FinishReasonSchema, finishMessage: z.string().optional(), custom: z.unknown(), }); @@ -226,7 +236,7 @@ export type CandidateError = z.infer; export const ModelResponseSchema = z.object({ message: MessageSchema.optional(), - finishReason: z.enum(['stop', 'length', 'blocked', 'other', 'unknown']), + finishReason: FinishReasonSchema, finishMessage: z.string().optional(), latencyMs: z.number().optional(), usage: GenerationUsageSchema.optional(), @@ -238,9 +248,7 @@ export type ModelResponseData = z.infer; export const GenerateResponseSchema = ModelResponseSchema.extend({ /** @deprecated All responses now return a single candidate. Only the first candidate will be used if supplied. Return `message`, `finishReason`, and `finishMessage` instead. */ candidates: z.array(CandidateSchema).optional(), - finishReason: z - .enum(['stop', 'length', 'blocked', 'other', 'unknown']) - .optional(), + finishReason: FinishReasonSchema.optional(), }); export type GenerateResponseData = z.infer; diff --git a/genkit-tools/genkit-schema.json b/genkit-tools/genkit-schema.json index 97ab1caf0..16e08ded9 100644 --- a/genkit-tools/genkit-schema.json +++ b/genkit-tools/genkit-schema.json @@ -297,14 +297,7 @@ "$ref": "#/$defs/GenerationUsage" }, "finishReason": { - "type": "string", - "enum": [ - "stop", - "length", - "blocked", - "other", - "unknown" - ] + "$ref": "#/$defs/FinishReason" }, "finishMessage": { "type": "string" @@ -341,6 +334,17 @@ }, "additionalProperties": false }, + "FinishReason": { + "type": "string", + "enum": [ + "stop", + "length", + "blocked", + "interrupted", + "other", + "unknown" + ] + }, "GenerateRequest": { "type": "object", "properties": { @@ -489,14 +493,7 @@ "$ref": "#/$defs/Message" }, "finishReason": { - "type": "string", - "enum": [ - "stop", - "length", - "blocked", - "other", - "unknown" - ] + "$ref": "#/$defs/FinishReason" }, "finishMessage": { "type": "string" @@ -755,14 +752,7 @@ "$ref": "#/$defs/GenerateResponse/properties/message" }, "finishReason": { - "type": "string", - "enum": [ - "stop", - "length", - "blocked", - "other", - "unknown" - ] + "$ref": "#/$defs/FinishReason" }, "finishMessage": { "$ref": "#/$defs/GenerateResponse/properties/finishMessage" diff --git a/js/ai/src/generate/action.ts b/js/ai/src/generate/action.ts index 99830bff9..8f3601a8e 100644 --- a/js/ai/src/generate/action.ts +++ b/js/ai/src/generate/action.ts @@ -51,7 +51,12 @@ import { ToolResponsePart, resolveModel, } from '../model.js'; -import { ToolAction, resolveTools, toToolDefinition } from '../tool.js'; +import { + ToolAction, + ToolInterruptError, + resolveTools, + toToolDefinition, +} from '../tool.js'; export const GenerateUtilParamSchema = z.object({ /** A model name (e.g. `vertexai/gemini-1.0-pro`). */ @@ -238,6 +243,8 @@ async function generate( let messages: MessageData[] = [...request.messages, message]; let newTools = options.rawRequest.tools; let newToolChoice = options.rawRequest.toolChoice; + let interruptedParts: Part[] = []; + let pendingToolRequests: Part[] = []; for (const part of toolCalls) { if (!part.toolRequest) { throw Error( @@ -249,29 +256,62 @@ async function generate( throw Error(`Tool ${part.toolRequest?.name} not found`); } if ((tool.__action.metadata.type as string) === 'prompt') { - const newPreamble = await tool(part.toolRequest?.input); - toolResponses.push({ - toolResponse: { - name: part.toolRequest.name, - ref: part.toolRequest.ref, - output: `transferred to ${part.toolRequest.name}`, - }, - }); - // swap out the preamble - messages = [ - ...tagAsPreamble(newPreamble.messages)!, - ...messages.filter((m) => !m?.metadata?.preamble), - ]; - newTools = newPreamble.tools; - newToolChoice = newPreamble.toolChoice; + try { + const newPreamble = await tool(part.toolRequest?.input); + toolResponses.push({ + toolResponse: { + name: part.toolRequest.name, + ref: part.toolRequest.ref, + output: `transferred to ${part.toolRequest.name}`, + }, + }); + // swap out the preamble + messages = [ + ...tagAsPreamble(newPreamble.messages)!, + ...messages.filter((m) => !m?.metadata?.preamble), + ]; + newTools = newPreamble.tools; + newToolChoice = newPreamble.toolChoice; + } catch (e) { + if (e instanceof ToolInterruptError) { + logger.debug(`interrupted tool ${part.toolRequest?.name}`); + part.metadata = { ...part.metadata, interrupt: e.metadata || true }; + interruptedParts.push(part); + } else { + throw e; + } + } } else { - toolResponses.push({ - toolResponse: { - name: part.toolRequest.name, - ref: part.toolRequest.ref, - output: await tool(part.toolRequest?.input), - }, - }); + try { + const toolOutput = await tool(part.toolRequest?.input); + toolResponses.push({ + toolResponse: { + name: part.toolRequest.name, + ref: part.toolRequest.ref, + output: toolOutput, + }, + }); + // we prep these in case any other tool gets interrupted. + pendingToolRequests.push({ + ...part, + metadata: { + ...part.metadata, + pendingToolResponse: { + name: part.toolRequest.name, + ref: part.toolRequest.ref, + output: toolOutput, + }, + }, + }); + } catch (e) { + if (e instanceof ToolInterruptError) { + logger.debug(`interrupted tool ${part.toolRequest?.name}`); + part.metadata = { ...part.metadata, interrupt: e.metadata || true }; + interruptedParts.push(part); + } else { + throw e; + } + } } } options.messageIndex++; @@ -301,6 +341,20 @@ async function generate( } ) ); + if (interruptedParts.length > 0) { + const nonToolParts = + (response.message?.content.filter((c) => !c.toolRequest) as Part[]) || []; + return { + ...response.toJSON(), + finishReason: 'interrupted', + message: { + role: 'model', + content: nonToolParts + .concat(pendingToolRequests) + .concat(interruptedParts), + }, + }; + } return await generateHelper(registry, { rawRequest: nextRequest, middleware: options.middleware, diff --git a/js/ai/src/index.ts b/js/ai/src/index.ts index 4a93c5cdd..8f734521e 100644 --- a/js/ai/src/index.ts +++ b/js/ai/src/index.ts @@ -108,6 +108,7 @@ export { type RetrieverReference, } from './retriever.js'; export { + ToolInterruptError, asTool, defineTool, type ToolAction, diff --git a/js/ai/src/model.ts b/js/ai/src/model.ts index e2241886e..9fb5f49f4 100644 --- a/js/ai/src/model.ts +++ b/js/ai/src/model.ts @@ -271,7 +271,7 @@ export type GenerationCommonConfig = typeof GenerationCommonConfigSchema; /** * Zod schema of output config. */ -const OutputConfigSchema = z.object({ +export const OutputConfigSchema = z.object({ format: z.string().optional(), schema: z.record(z.any()).optional(), constrained: z.boolean().optional(), @@ -344,12 +344,22 @@ export const GenerationUsageSchema = z.object({ */ export type GenerationUsage = z.infer; +/** Model response finish reason enum. */ +const FinishReasonSchema = z.enum([ + 'stop', + 'length', + 'blocked', + 'interrupted', + 'other', + 'unknown', +]); + /** @deprecated All responses now return a single candidate. Only the first candidate will be used if supplied. */ export const CandidateSchema = z.object({ index: z.number(), message: MessageSchema, usage: GenerationUsageSchema.optional(), - finishReason: z.enum(['stop', 'length', 'blocked', 'other', 'unknown']), + finishReason: FinishReasonSchema, finishMessage: z.string().optional(), custom: z.unknown(), }); @@ -370,7 +380,7 @@ export type CandidateError = z.infer; */ export const ModelResponseSchema = z.object({ message: MessageSchema.optional(), - finishReason: z.enum(['stop', 'length', 'blocked', 'other', 'unknown']), + finishReason: FinishReasonSchema, finishMessage: z.string().optional(), latencyMs: z.number().optional(), usage: GenerationUsageSchema.optional(), @@ -391,9 +401,7 @@ export type ModelResponseData = z.infer; export const GenerateResponseSchema = ModelResponseSchema.extend({ /** @deprecated All responses now return a single candidate. Only the first candidate will be used if supplied. Return `message`, `finishReason`, and `finishMessage` instead. */ candidates: z.array(CandidateSchema).optional(), - finishReason: z - .enum(['stop', 'length', 'blocked', 'other', 'unknown']) - .optional(), + finishReason: FinishReasonSchema.optional(), }); /** diff --git a/js/ai/src/tool.ts b/js/ai/src/tool.ts index 33a9d4645..590b6645e 100644 --- a/js/ai/src/tool.ts +++ b/js/ai/src/tool.ts @@ -166,6 +166,19 @@ export function toToolDefinition( return out; } +export interface ToolFnOptions { + /** + * A function that can be called during tool execution that will result in the tool + * getting interrupted (immediately) and tool request returned to the upstream caller. + */ + interrupt: (metadata?: Record) => void; +} + +export type ToolFn = ( + input: z.infer, + ctx: ToolFnOptions +) => Promise>; + /** * Defines a tool. * @@ -174,7 +187,7 @@ export function toToolDefinition( export function defineTool( registry: Registry, config: ToolConfig, - fn: (input: z.infer) => Promise> + fn: ToolFn ): ToolAction { const a = defineAction( registry, @@ -183,7 +196,27 @@ export function defineTool( actionType: 'tool', metadata: { ...(config.metadata || {}), type: 'tool' }, }, - (i) => fn(i) + (i) => + fn(i, { + interrupt: interruptTool, + }) ); return a as ToolAction; } + +/** + * Thrown when tools execution is interrupted. It's meant to be caugh by the framework, not public API. + */ +export class ToolInterruptError extends Error { + constructor(readonly metadata?: Record) { + super(); + } +} + +/** + * Interrupts current tool execution causing tool request to be returned in the generation response. + * Should only be called within a tool. + */ +function interruptTool(metadata?: Record) { + throw new ToolInterruptError(metadata); +} diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index b2006722c..89b12e0ae 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -102,7 +102,7 @@ import { SessionError, SessionOptions, } from '@genkit-ai/ai/session'; -import { resolveTools } from '@genkit-ai/ai/tool'; +import { resolveTools, ToolFn } from '@genkit-ai/ai/tool'; import { Action, defineFlow, @@ -218,7 +218,7 @@ export class Genkit implements HasRegistry { */ defineTool( config: ToolConfig, - fn: (input: z.infer) => Promise> + fn: ToolFn ): ToolAction { return defineTool(this.registry, config, fn); } diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index af5c0cb31..45e3abe55 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -560,5 +560,154 @@ describe('generate', () => { } ); }); + + it('interrupts tool execution', async () => { + ai.defineTool( + { name: 'simpleTool', description: 'description' }, + async (input) => `response: ${input.name}` + ); + ai.defineTool( + { name: 'interruptingTool', description: 'description' }, + async (input, { interrupt }) => + interrupt({ confirm: 'is it a banana?' }) + ); + + // first response be tools call, the subsequent just text response from agent b. + let reqCounter = 0; + pm.handleResponse = async (req, sc) => { + return { + message: { + role: 'model', + content: + reqCounter++ === 0 + ? [ + { + text: 'reasoning', + }, + { + toolRequest: { + name: 'interruptingTool', + input: {}, + ref: 'ref123', + }, + }, + { + toolRequest: { + name: 'simpleTool', + input: { name: 'foo' }, + ref: 'ref123', + }, + }, + ] + : [{ text: 'done' }], + }, + }; + }; + + const response = await ai.generate({ + prompt: 'call the tool', + tools: ['interruptingTool', 'simpleTool'], + }); + + assert.strictEqual(reqCounter, 1); + assert.deepStrictEqual(response.toolRequests, [ + { + metadata: { + pendingToolResponse: { + name: 'simpleTool', + output: 'response: foo', + ref: 'ref123', + }, + }, + toolRequest: { + input: { + name: 'foo', + }, + name: 'simpleTool', + ref: 'ref123', + }, + }, + { + toolRequest: { + input: {}, + name: 'interruptingTool', + ref: 'ref123', + }, + metadata: { + interrupt: { + confirm: 'is it a banana?', + }, + }, + }, + ]); + assert.deepStrictEqual(response.message?.toJSON(), { + role: 'model', + content: [ + { + text: 'reasoning', + }, + { + metadata: { + pendingToolResponse: { + name: 'simpleTool', + output: 'response: foo', + ref: 'ref123', + }, + }, + toolRequest: { + input: { + name: 'foo', + }, + name: 'simpleTool', + ref: 'ref123', + }, + }, + { + metadata: { + interrupt: { + confirm: 'is it a banana?', + }, + }, + toolRequest: { + input: {}, + name: 'interruptingTool', + ref: 'ref123', + }, + }, + ], + }); + assert.deepStrictEqual(pm.lastRequest, { + config: {}, + messages: [ + { + role: 'user', + content: [{ text: 'call the tool' }], + }, + ], + output: {}, + tools: [ + { + description: 'description', + inputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + name: 'interruptingTool', + outputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + }, + { + description: 'description', + inputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + name: 'simpleTool', + outputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + }, + ], + }); + }); }); }); From e2eef37c669fb1d133672c8432799dc2d94d7ee0 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:00:50 +0000 Subject: [PATCH 241/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.5 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 7907e1a0d..7b23e6f0b 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 7df8a25e5c5d1fd990975dd529b6e4c6e67c4348 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:00:52 +0000 Subject: [PATCH 242/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.5 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 0644d21a4..1af4b9527 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From defb2415a26943c08ee4152b82110ea5baf4d5e1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:00:55 +0000 Subject: [PATCH 243/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.5 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index 1166bfae3..372b2377d 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From b840e793c5014db948de987da0ded9e575fe1915 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:02:31 +0000 Subject: [PATCH 244/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.5 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index d85156f5c..d0b2327bb 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From 5424be7fdabab5588ad5a7f592abe0d2a2b678aa Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:02:33 +0000 Subject: [PATCH 245/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.5 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index 9c4a301e4..aa7647369 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From e76401d37cc6f0c77b982cd96cd1894ed7051945 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:02:35 +0000 Subject: [PATCH 246/562] chore: bump genkit version to genkit@1.0.0-rc.5 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index c4f408370..e74abfe11 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From 1d3e19483097d8fe55c09ebb49d27630d6440268 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:02:37 +0000 Subject: [PATCH 247/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.5 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index 55ebf3e72..b04712dd8 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From 18f3d6f88f339396f04ea8997eaa5daf21b97c6a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:02:39 +0000 Subject: [PATCH 248/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.5 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 9e97b3266..cd65a928a 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From c80287f29a037f5b3a2e917b5bd762f21a9945ce Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:02:42 +0000 Subject: [PATCH 249/562] chore: bump @genkit-ai/dotprompt version to @genkit-ai/dotprompt@1.0.0-rc.5 --- js/plugins/dotprompt/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dotprompt/package.json b/js/plugins/dotprompt/package.json index 28d818330..b4f1621bf 100644 --- a/js/plugins/dotprompt/package.json +++ b/js/plugins/dotprompt/package.json @@ -9,7 +9,7 @@ "prompting", "templating" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From d27db566c9b8766bd5715085c8f141a5611cba87 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:02:44 +0000 Subject: [PATCH 250/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.5 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 306eab32a..2bcc01d55 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From 9fb53984f3780ddf0d6e6383e873c9b99465815e Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:02:46 +0000 Subject: [PATCH 251/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.5 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index a2a0d4934..7018caec5 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From 9fc910d11e0a13f86518354c84b8e8b0842dbd9a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:02:48 +0000 Subject: [PATCH 252/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.5 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index 68137e790..4863423fb 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From 41dce7a30fb27842a12378bc2f30ff4df54945ea Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:02:51 +0000 Subject: [PATCH 253/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.5 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index ec0976a0f..daab344dd 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From 1a509c52c63a8c69dfbe93537b6612031f9e8f39 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:02:53 +0000 Subject: [PATCH 254/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.5 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index 58c1f9ff8..1bd3353cd 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From 5fe045aee0b61117045ff929593e9c99e9d97c00 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:02:55 +0000 Subject: [PATCH 255/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.5 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index 9cbae167b..317353a0b 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From 740f3a352d33cdfc5b4577ca850d3ea1a0744f9a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:02:57 +0000 Subject: [PATCH 256/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.5 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index c186dc076..559d052d7 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From 06975890e92602c0a1a072702f2d72bc0e8ad3a7 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:03:00 +0000 Subject: [PATCH 257/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.5 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 73fc7e6d9..7bc8a06d0 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From 3fc0d5a7ce741016931a72fccd0483e7a3d610ba Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:03:02 +0000 Subject: [PATCH 258/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.5 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index f9e71b15d..64f36abbb 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From 8a39b1bca523e8af46b0210d67e89e3139632e48 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:03:05 +0000 Subject: [PATCH 259/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.5 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index d4917f653..03e12e340 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From 1bd8c755ffbd370d7fd04ea412351f55c0a1d1f3 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 23 Jan 2025 20:03:07 +0000 Subject: [PATCH 260/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.5 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index 27e051833..5ae6ce3f0 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "type": "commonjs", "scripts": { "check": "tsc", From 46b28afb73a2a01a49d433df5ca7c00b4bdc7dd9 Mon Sep 17 00:00:00 2001 From: thedmail Date: Thu, 23 Jan 2025 17:17:38 -0800 Subject: [PATCH 261/562] Docs: Update firebase.md (#1653) Docs: Replaces internal-only URL with a link accessible to non-Google developers. --- docs/plugins/firebase.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/firebase.md b/docs/plugins/firebase.md index 23cf74ce2..d5aeb81ba 100644 --- a/docs/plugins/firebase.md +++ b/docs/plugins/firebase.md @@ -65,7 +65,7 @@ To provide Firebase credentials, you also need to set up Google Cloud Applicatio Firebase Genkit Monitoring is powered by Google's Cloud operation suite. This requires telemetry related API's to be enabled for your project. Please refer to the [Google Cloud plugin](google-cloud.md#set-up-a-google-cloud-account) documentation for more details. -Grant the following roles to the **"Default compute service account"** within the [Google Cloud IAM Console](https://pantheon.corp.google.com/iam-admin/iam?authuser=0): +Grant the following roles to the **"Default compute service account"** within the [Google Cloud IAM Console](https://console.cloud.google.com/iam-admin/iam-admin/iam?authuser=0): - **Monitoring Metric Writer** (roles/monitoring.metricWriter) - **Cloud Trace Agent** (roles/cloudtrace.agent) @@ -345,4 +345,4 @@ export const exampleFlow = onFlow( To define an auth policy, provide `firebaseAuth()` with a callback function that takes a [`DecodedIdToken`](https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.decodedidtoken) as its only parameter. In this function, examine the user token and throw an error if the user fails to meet any of the criteria you want to require. -See [Authorization and integrity](http://../auth.md) for a more thorough discussion of this topic. \ No newline at end of file +See [Authorization and integrity](http://../auth.md) for a more thorough discussion of this topic. From f776e7a6e271729b65bf8f09e13572253a30d993 Mon Sep 17 00:00:00 2001 From: thedmail Date: Fri, 24 Jan 2025 06:18:42 -0800 Subject: [PATCH 262/562] Docs: Update google-cloud.md (#1654) Docs: Replaces internal URL with one accessible to non-Google developers. --- docs/plugins/google-cloud.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/google-cloud.md b/docs/plugins/google-cloud.md index 093047e2b..7f2b9fd50 100644 --- a/docs/plugins/google-cloud.md +++ b/docs/plugins/google-cloud.md @@ -62,7 +62,7 @@ automatically via You will need to apply the following roles to the service account that is running your code (i.e. 'attached service account') via the -[IAM Console](https://pantheon.corp.google.com/iam-admin/iam): +[IAM Console](https://console.cloud.google.com/iam-admin/iam): - `roles/monitoring.metricWriter` - `roles/cloudtrace.agent` From e91bc0d21948a96454a116c2cff2dbbe88283f2b Mon Sep 17 00:00:00 2001 From: thedmail Date: Fri, 24 Jan 2025 11:11:22 -0800 Subject: [PATCH 263/562] Docs: Update firebase.md (#1655) Fixes internal-linter violation. --- docs/plugins/firebase.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/firebase.md b/docs/plugins/firebase.md index d5aeb81ba..d0f26b56a 100644 --- a/docs/plugins/firebase.md +++ b/docs/plugins/firebase.md @@ -65,7 +65,7 @@ To provide Firebase credentials, you also need to set up Google Cloud Applicatio Firebase Genkit Monitoring is powered by Google's Cloud operation suite. This requires telemetry related API's to be enabled for your project. Please refer to the [Google Cloud plugin](google-cloud.md#set-up-a-google-cloud-account) documentation for more details. -Grant the following roles to the **"Default compute service account"** within the [Google Cloud IAM Console](https://console.cloud.google.com/iam-admin/iam-admin/iam?authuser=0): +Grant the following roles to the **"Default compute service account"** within the [Google Cloud IAM Console](https://console.cloud.google.com/iam-admin/iam-admin/iam): - **Monitoring Metric Writer** (roles/monitoring.metricWriter) - **Cloud Trace Agent** (roles/cloudtrace.agent) From 378fc0885239213a119dcb1bfebf8f41f3f8402c Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Fri, 24 Jan 2025 13:28:00 -0800 Subject: [PATCH 264/562] feat(js/ai): adds defineInterrupt for more ergonomic interrupts (#1658) --- js/ai/src/index.ts | 2 + js/ai/src/tool.ts | 50 +++++++++++++- js/ai/tests/prompt/prompt_test.ts | 2 +- js/ai/tests/tool_test.ts | 105 ++++++++++++++++++++++++++++++ js/genkit/src/genkit.ts | 14 ++++ js/genkit/src/index.ts | 1 + 6 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 js/ai/tests/tool_test.ts diff --git a/js/ai/src/index.ts b/js/ai/src/index.ts index 8f734521e..5e859d77a 100644 --- a/js/ai/src/index.ts +++ b/js/ai/src/index.ts @@ -110,7 +110,9 @@ export { export { ToolInterruptError, asTool, + defineInterrupt, defineTool, + type InterruptConfig, type ToolAction, type ToolArgument, type ToolConfig, diff --git a/js/ai/src/tool.ts b/js/ai/src/tool.ts index 590b6645e..1ae92d1b6 100644 --- a/js/ai/src/tool.ts +++ b/js/ai/src/tool.ts @@ -171,7 +171,7 @@ export interface ToolFnOptions { * A function that can be called during tool execution that will result in the tool * getting interrupted (immediately) and tool request returned to the upstream caller. */ - interrupt: (metadata?: Record) => void; + interrupt: (metadata?: Record) => never; } export type ToolFn = ( @@ -204,12 +204,58 @@ export function defineTool( return a as ToolAction; } +/** InterruptConfig defines the options for configuring an interrupt. */ +export type InterruptConfig< + I extends z.ZodTypeAny = z.ZodTypeAny, + R extends z.ZodTypeAny = z.ZodTypeAny, +> = Omit, 'outputSchema' | 'outputJsonSchema'> & { + /** replySchema defines the Zod schema that this interrupt needs when resuming */ + replySchema?: R; + /** replyJsonSchema defines the JSON schema that this interrupt needs when resuming */ + replyJsonSchema?: JSONSchema7; + /** requestMetadata adds additional `interrupt` metadata to the `toolRequest` generated by the interrupt */ + requestMetadata?: + | Record + | (( + input: z.infer + ) => Record | Promise>); +}; + +export function defineInterrupt( + registry: Registry, + config: InterruptConfig +): ToolAction { + const { + replySchema: outputSchema, + replyJsonSchema: outputJsonSchema, + requestMetadata, + ...overlap + } = config; + const toolConfig: ToolConfig = { + ...overlap, + outputSchema, + outputJsonSchema, + }; + + return defineTool( + registry, + toolConfig, + async (input, { interrupt }) => { + if (!config.requestMetadata) interrupt(); + else if (typeof config.requestMetadata === 'object') + interrupt(config.requestMetadata); + else interrupt(await Promise.resolve(config.requestMetadata(input))); + } + ); +} + /** * Thrown when tools execution is interrupted. It's meant to be caugh by the framework, not public API. */ export class ToolInterruptError extends Error { constructor(readonly metadata?: Record) { super(); + this.name = 'ToolInterruptError'; } } @@ -217,6 +263,6 @@ export class ToolInterruptError extends Error { * Interrupts current tool execution causing tool request to be returned in the generation response. * Should only be called within a tool. */ -function interruptTool(metadata?: Record) { +function interruptTool(metadata?: Record): never { throw new ToolInterruptError(metadata); } diff --git a/js/ai/tests/prompt/prompt_test.ts b/js/ai/tests/prompt/prompt_test.ts index 9c2d40d8d..db26ee42e 100644 --- a/js/ai/tests/prompt/prompt_test.ts +++ b/js/ai/tests/prompt/prompt_test.ts @@ -18,7 +18,7 @@ import { z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; import * as assert from 'assert'; import { describe, it } from 'node:test'; -import { definePrompt, renderPrompt } from '../../src/prompt.ts'; +import { definePrompt, renderPrompt } from '../../src/prompt.js'; describe('prompt', () => { let registry = new Registry(); diff --git a/js/ai/tests/tool_test.ts b/js/ai/tests/tool_test.ts new file mode 100644 index 000000000..0fcf461d6 --- /dev/null +++ b/js/ai/tests/tool_test.ts @@ -0,0 +1,105 @@ +/** + * 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. + */ + +import { z } from '@genkit-ai/core'; +import { Registry } from '@genkit-ai/core/registry'; +import * as assert from 'assert'; +import { afterEach, describe, it } from 'node:test'; +import { defineInterrupt } from '../src/tool.js'; + +describe('defineInterrupt', () => { + let registry = new Registry(); + + afterEach(() => { + registry = new Registry(); + }); + + function expectInterrupt(fn: () => any, metadata?: Record) { + assert.rejects(fn, { name: 'ToolInterruptError', metadata }); + } + + it('should throw a simple interrupt with no metadata', () => { + const simple = defineInterrupt(registry, { + name: 'simple', + description: 'simple interrupt', + }); + expectInterrupt(async () => { + await simple({}); + }); + }); + + it('should throw a simple interrupt with fixed metadata', () => { + const simple = defineInterrupt(registry, { + name: 'simple', + description: 'simple interrupt', + requestMetadata: { foo: 'bar' }, + }); + expectInterrupt( + async () => { + await simple({}); + }, + { foo: 'bar' } + ); + }); + + it('should throw a simple interrupt with function-returned metadata', () => { + const simple = defineInterrupt(registry, { + name: 'simple', + description: 'simple interrupt', + inputSchema: z.string(), + requestMetadata: (foo) => ({ foo }), + }); + expectInterrupt( + async () => { + await simple('bar'); + }, + { foo: 'bar' } + ); + }); + + it('should throw a simple interrupt with async function-returned metadata', () => { + const simple = defineInterrupt(registry, { + name: 'simple', + description: 'simple interrupt', + inputSchema: z.string(), + requestMetadata: async (foo) => ({ foo }), + }); + expectInterrupt( + async () => { + await simple('bar'); + }, + { foo: 'bar' } + ); + }); + + it('should register the reply schema / json schema as the output schema of the tool', () => { + const ReplySchema = z.object({ foo: z.string() }); + const simple = defineInterrupt(registry, { + name: 'simple', + description: 'simple', + replySchema: ReplySchema, + }); + assert.equal(simple.__action.outputSchema, ReplySchema); + const simple2 = defineInterrupt(registry, { + name: 'simple2', + description: 'simple2', + replyJsonSchema: { type: 'string' }, + }); + assert.deepStrictEqual(simple2.__action.outputJsonSchema, { + type: 'string', + }); + }); +}); diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index 89b12e0ae..82da56a41 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -16,6 +16,7 @@ import { BaseDataPointSchema, + defineInterrupt, definePrompt, defineTool, Document, @@ -37,6 +38,7 @@ import { GenerateStreamResponse, GenerationCommonConfigSchema, IndexerParams, + InterruptConfig, isExecutablePrompt, ModelArgument, ModelReference, @@ -223,6 +225,18 @@ export class Genkit implements HasRegistry { return defineTool(this.registry, config, fn); } + /** + * Defines and registers an interrupt. + * + * Interrupts are special tools that halt model processing and return control back to the caller. Interrupts make it simpler to implement + * "human-in-the-loop" and out-of-band processing patterns that require waiting on external actions to complete. + */ + defineInterrupt( + config: InterruptConfig + ): ToolAction { + return defineInterrupt(this.registry, config); + } + /** * Defines and registers a schema from a Zod schema. * diff --git a/js/genkit/src/index.ts b/js/genkit/src/index.ts index e7a7032fc..cbbef6c05 100644 --- a/js/genkit/src/index.ts +++ b/js/genkit/src/index.ts @@ -72,6 +72,7 @@ export { type IndexerInfo, type IndexerParams, type IndexerReference, + type InterruptConfig, type LlmResponse, type LlmStats, type MediaPart, From a82808373fd9f3cd5dcefe9110bbc8a2f3acfdc2 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Fri, 24 Jan 2025 21:25:35 -0500 Subject: [PATCH 265/562] =?UTF-8?q?breaking(js/ai):=20refactored=20prompts?= =?UTF-8?q?=20API,=20migrated=20to=20standalone=20dotpr=E2=80=A6=20(#1651)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/bump-js-version.yml | 11 - js/ai/package.json | 4 +- js/ai/src/generate.ts | 12 +- js/ai/src/index.ts | 6 +- js/ai/src/prompt.ts | 659 ++++++++-- js/ai/src/session.ts | 8 +- js/ai/src/tool.ts | 3 +- js/ai/tests/prompt/prompt_test.ts | 636 +++++++++- js/core/package.json | 3 +- js/core/src/registry.ts | 17 +- js/core/src/utils.ts | 20 + js/doc-snippets/src/dotprompt/index.ts | 64 +- js/doc-snippets/src/multi-agent/multi.ts | 30 +- js/genkit/package.json | 1 - js/genkit/src/genkit.ts | 406 ++---- js/genkit/src/index.ts | 9 +- js/genkit/tests/chat_test.ts | 142 ++- js/genkit/tests/prompts/kitchensink.prompt | 25 + js/genkit/tests/prompts_test.ts | 1092 ++++++++--------- js/genkit/tests/session_test.ts | 72 +- js/package.json | 5 +- js/plugins/dotprompt/.npmignore | 1 - js/plugins/dotprompt/LICENSE | 203 --- js/plugins/dotprompt/README.md | 7 - js/plugins/dotprompt/package.json | 54 - js/plugins/dotprompt/src/index.ts | 87 -- js/plugins/dotprompt/src/metadata.ts | 205 ---- js/plugins/dotprompt/src/picoschema.ts | 135 -- js/plugins/dotprompt/src/prompt.ts | 436 ------- js/plugins/dotprompt/src/registry.ts | 157 --- js/plugins/dotprompt/src/template.ts | 188 --- js/plugins/dotprompt/tests/picoschema_test.ts | 31 - .../dotprompt/tests/picoschema_tests.yaml | 173 --- js/plugins/dotprompt/tests/prompt_test.ts | 527 -------- js/plugins/dotprompt/tests/template_test.ts | 200 --- js/plugins/dotprompt/tsconfig.json | 4 - js/plugins/dotprompt/tsup.config.ts | 22 - js/plugins/dotprompt/typedoc.json | 3 - js/plugins/evaluators/package.json | 4 +- .../src/metrics/answer_relevancy.ts | 6 +- .../evaluators/src/metrics/faithfulness.ts | 9 +- js/plugins/evaluators/src/metrics/helper.ts | 21 + .../evaluators/src/metrics/maliciousness.ts | 6 +- js/plugins/mcp/src/client/prompts.ts | 22 +- js/pnpm-lock.yaml | 132 +- .../src/deliciousness/deliciousness.ts | 20 +- .../src/funniness/funniness.ts | 20 +- .../src/pii/pii_detection.ts | 21 +- .../dev-ui-gallery/src/main/prompts.ts | 147 +-- js/testapps/dev-ui-gallery/src/main/tools.ts | 39 +- .../flow-simple-ai/prompts/TellJoke.prompt | 6 +- js/testapps/menu/package.json | 1 - js/testapps/menu/src/01/prompts.ts | 49 +- js/testapps/menu/src/02/prompts.ts | 24 +- js/testapps/menu/src/04/prompts.ts | 20 +- js/testapps/menu/src/05/prompts.ts | 46 +- js/testapps/rag/src/prompt.ts | 30 +- scripts/release_main.sh | 4 - scripts/release_next.sh | 4 - 59 files changed, 2242 insertions(+), 4047 deletions(-) create mode 100644 js/genkit/tests/prompts/kitchensink.prompt delete mode 100644 js/plugins/dotprompt/.npmignore delete mode 100644 js/plugins/dotprompt/LICENSE delete mode 100644 js/plugins/dotprompt/README.md delete mode 100644 js/plugins/dotprompt/package.json delete mode 100644 js/plugins/dotprompt/src/index.ts delete mode 100644 js/plugins/dotprompt/src/metadata.ts delete mode 100644 js/plugins/dotprompt/src/picoschema.ts delete mode 100644 js/plugins/dotprompt/src/prompt.ts delete mode 100644 js/plugins/dotprompt/src/registry.ts delete mode 100644 js/plugins/dotprompt/src/template.ts delete mode 100644 js/plugins/dotprompt/tests/picoschema_test.ts delete mode 100644 js/plugins/dotprompt/tests/picoschema_tests.yaml delete mode 100644 js/plugins/dotprompt/tests/prompt_test.ts delete mode 100644 js/plugins/dotprompt/tests/template_test.ts delete mode 100644 js/plugins/dotprompt/tsconfig.json delete mode 100644 js/plugins/dotprompt/tsup.config.ts delete mode 100644 js/plugins/dotprompt/typedoc.json diff --git a/.github/workflows/bump-js-version.yml b/.github/workflows/bump-js-version.yml index 345c64b4a..ed1c7f327 100644 --- a/.github/workflows/bump-js-version.yml +++ b/.github/workflows/bump-js-version.yml @@ -107,17 +107,6 @@ jobs: preid: ${{ inputs.preid }} commit-message: 'chore: bump @genkit-ai/dev-local-vectorstore version to {{version}}' tag-prefix: '@genkit-ai/dev-local-vectorstore@' - - name: 'js/plugins/dotprompt version bump' - uses: 'phips28/gh-action-bump-version@master' - env: - GITHUB_TOKEN: ${{ secrets.GENKIT_RELEASER_GITHUB_TOKEN }} - PACKAGEJSON_DIR: js/plugins/dotprompt - with: - default: ${{ inputs.releaseType }} - version-type: ${{ inputs.releaseType }} - preid: ${{ inputs.preid }} - commit-message: 'chore: bump @genkit-ai/dotprompt version to {{version}}' - tag-prefix: '@genkit-ai/dotprompt@' - name: 'js/plugins/evaluators version bump' uses: 'phips28/gh-action-bump-version@master' env: diff --git a/js/ai/package.json b/js/ai/package.json index aa7647369..757db31df 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -16,6 +16,7 @@ "build": "npm-run-all build:clean check compile", "build:watch": "tsup-node --watch", "test": "node --import tsx --test ./tests/**/*_test.ts ./tests/*_test.ts", + "test:watch": "node --watch --import tsx --test ./tests/**/*_test.ts ./tests/*_test.ts", "test:single": "node --import tsx --test" }, "repository": { @@ -33,7 +34,8 @@ "json5": "^2.2.3", "node-fetch": "^3.3.2", "partial-json": "^0.1.7", - "uuid": "^10.0.0" + "uuid": "^10.0.0", + "dotprompt": "^1.0.0-dev || ^1" }, "devDependencies": { "npm-run-all": "^4.1.5", diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index 7443fcec3..93e4f114c 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -346,6 +346,8 @@ async function resolveFullToolName( return `/tool/${name}`; } else if (await registry.lookupAction(`/prompt/${name}`)) { return `/prompt/${name}`; + } else if (await registry.lookupAction(`/prompt/dotprompt/${name}`)) { + return `/prompt/dotprompt/${name}`; } else { throw new Error(`Unable to determine type of of tool: ${name}`); } @@ -372,10 +374,12 @@ export function generateStream< ): GenerateStreamResponse { let channel = new Channel(); - const generated = generate(registry, { - ...options, - onChunk: (chunk) => channel.send(chunk), - }); + const generated = Promise.resolve(options).then((resolvedOptions) => + generate(registry, { + ...resolvedOptions, + onChunk: (chunk) => channel.send(chunk), + }) + ); generated.then( () => channel.close(), (err) => channel.error(err) diff --git a/js/ai/src/index.ts b/js/ai/src/index.ts index 5e859d77a..2176b46ca 100644 --- a/js/ai/src/index.ts +++ b/js/ai/src/index.ts @@ -72,13 +72,15 @@ export { type ToolResponsePart, } from './model.js'; export { + defineHelper, + definePartial, definePrompt, isExecutablePrompt, - renderPrompt, + loadPromptFolder, + prompt, type ExecutablePrompt, type PromptAction, type PromptConfig, - type PromptFn, type PromptGenerateOptions, } from './prompt.js'; export { diff --git a/js/ai/src/prompt.ts b/js/ai/src/prompt.ts index d434e3575..dfb1ca79a 100644 --- a/js/ai/src/prompt.ts +++ b/js/ai/src/prompt.ts @@ -14,79 +14,117 @@ * limitations under the License. */ -import { Action, defineAction, JSONSchema7, z } from '@genkit-ai/core'; +import { + Action, + defineAction, + GenkitError, + JSONSchema7, + stripUndefinedProps, + z, +} from '@genkit-ai/core'; +import { logger } from '@genkit-ai/core/logging'; import { Registry } from '@genkit-ai/core/registry'; +import { toJsonSchema } from '@genkit-ai/core/schema'; +import { Message as DpMessage, PromptFunction } from 'dotprompt'; +import { existsSync, readdirSync, readFileSync } from 'fs'; +import { basename, join, resolve } from 'path'; import { DocumentData } from './document.js'; import { + generate, GenerateOptions, GenerateResponse, + generateStream, GenerateStreamResponse, + OutputOptions, + toGenerateRequest, ToolChoice, } from './generate.js'; +import { Message } from './message.js'; import { GenerateRequest, GenerateRequestSchema, GenerateResponseChunkSchema, + GenerateResponseSchema, + MessageData, + ModelAction, ModelArgument, ModelMiddleware, + ModelReference, + Part, } from './model.js'; +import { getCurrentSession, Session } from './session.js'; import { ToolAction, ToolArgument } from './tool.js'; -/** - * Prompt implementation function signature. - */ -export type PromptFn< - I extends z.ZodTypeAny = z.ZodTypeAny, - CustomOptionsSchema extends z.ZodTypeAny = z.ZodTypeAny, -> = (input: z.infer) => Promise>; - /** * Prompt action. */ export type PromptAction = Action< I, typeof GenerateRequestSchema, - typeof GenerateResponseChunkSchema + z.ZodNever > & { __action: { metadata: { type: 'prompt'; }; }; - __config: PromptConfig; + __executablePrompt: ExecutablePrompt; }; +/** + * Prompt action. + */ +export type ExecutablePromptAction = + Action< + I, + typeof GenerateResponseSchema, + typeof GenerateResponseChunkSchema + > & { + __action: { + metadata: { + type: 'executablePrompt'; + }; + }; + __executablePrompt: ExecutablePrompt; + }; + /** * Configuration for a prompt action. */ -export interface PromptConfig { +export interface PromptConfig< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, +> { name: string; + variant?: string; + model?: ModelArgument; + config?: z.infer; description?: string; - inputSchema?: I; - inputJsonSchema?: JSONSchema7; + input?: { + schema?: I; + jsonSchema?: JSONSchema7; + }; + system?: string | Part | Part[] | PartsResolver>; + prompt?: string | Part | Part[] | PartsResolver>; + messages?: string | MessageData[] | MessagesResolver>; + docs?: DocumentData[] | DocsResolver>; + output?: OutputOptions; + maxTurns?: number; + returnToolRequests?: boolean; metadata?: Record; tools?: ToolArgument[]; toolChoice?: ToolChoice; use?: ModelMiddleware[]; } -/** - * Checks whether provided object is a prompt. - */ -export function isPrompt(arg: any): boolean { - return ( - typeof arg === 'function' && - (arg as any).__action?.metadata?.type === 'prompt' - ); -} - /** * Generate options of a prompt. */ export type PromptGenerateOptions< O extends z.ZodTypeAny = z.ZodTypeAny, CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, -> = Omit, 'prompt'>; +> = Omit, 'prompt' | 'system'>; /** * A prompt that can be executed as a function. @@ -117,7 +155,7 @@ export interface ExecutablePrompt< stream( input?: I, opts?: PromptGenerateOptions - ): Promise>>; + ): GenerateStreamResponse>; /** * Renders the prompt template based on user input. @@ -126,9 +164,8 @@ export interface ExecutablePrompt< * @returns a `GenerateOptions` object to be used with the `generate()` function from @genkit-ai/ai. */ render( - opt: PromptGenerateOptions & { - input?: I; - } + input?: I, + opts?: PromptGenerateOptions ): Promise>; /** @@ -137,79 +174,379 @@ export interface ExecutablePrompt< asTool(): Promise; } +export type PartsResolver = ( + input: I, + options: { + state?: S; + } +) => Part[] | Promise; + +export type MessagesResolver = ( + input: I, + options: { + history?: MessageData[]; + state?: S; + } +) => MessageData[] | Promise; + +export type DocsResolver = ( + input: I, + options: { + state?: S; + } +) => DocumentData[] | Promise; + +interface PromptCache { + userPrompt?: PromptFunction; + system?: PromptFunction; + messages?: PromptFunction; +} + /** - * Defines and registers a prompt action. The action can be called to obtain - * a `GenerateRequest` which can be passed to a model action. The given - * `PromptFn` can perform any action needed to create the request such as rendering - * a template or fetching a prompt from a database. + * Defines a prompt which can be used to generate content or render a request. * - * @returns The new `PromptAction`. + * @returns The new `ExecutablePrompt`. */ -export function definePrompt( +export function definePrompt< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, +>( registry: Registry, - config: PromptConfig, - fn: PromptFn -): PromptAction { - const a = defineAction( + options: PromptConfig +): ExecutablePrompt, O, CustomOptions> { + const promptCache = {} as PromptCache; + + const renderOptionsFn = async ( + input: z.infer, + renderOptions: PromptGenerateOptions | undefined + ): Promise => { + const messages: MessageData[] = []; + renderOptions = { ...renderOptions }; // make a copy, we will be trimming + const session = getCurrentSession(registry); + + // order of these matters: + await renderSystemPrompt( + registry, + session, + input, + messages, + options, + promptCache + ); + await renderMessages( + registry, + session, + input, + messages, + options, + renderOptions, + promptCache + ); + await renderUserPrompt( + registry, + session, + input, + messages, + options, + promptCache + ); + + let docs: DocumentData[] | undefined; + if (typeof options.docs === 'function') { + docs = await options.docs(input, { + state: session?.state, + }); + } else { + docs = options.docs; + } + + return stripUndefinedProps({ + model: options.model, + maxTurns: options.maxTurns, + messages, + docs, + tools: options.tools, + returnToolRequests: options.returnToolRequests, + toolChoice: options.toolChoice, + output: options.output, + use: options.use, + ...stripUndefinedProps(renderOptions), + config: { + ...options?.config, + ...renderOptions?.config, + }, + }); + }; + const rendererAction = defineAction( registry, { - name: config.name, - inputJsonSchema: config.inputJsonSchema, - inputSchema: config.inputSchema, - description: config.description, + name: `${options.name}${options.variant ? `.${options.variant}` : ''}`, + inputJsonSchema: options.input?.jsonSchema, + inputSchema: options.input?.schema, + description: options.description, actionType: 'prompt', - metadata: { ...(config.metadata || { prompt: {} }), type: 'prompt' }, + metadata: { + prompt: { + config: options.config, + input: { + schema: options.input ? toJsonSchema(options.input) : undefined, + }, + name: options.name, + model: modelName(options.model), + }, + ...options.metadata, + type: 'prompt', + }, }, - fn + async (input: z.infer): Promise> => { + return toGenerateRequest( + registry, + await renderOptionsFn(input, undefined) + ); + } + ) as PromptAction; + const executablePromptAction = defineAction( + registry, + { + name: `${options.name}${options.variant ? `.${options.variant}` : ''}`, + inputJsonSchema: options.input?.jsonSchema, + inputSchema: options.input?.schema, + description: options.description, + actionType: 'executable-prompt', + metadata: { ...(options.metadata || { prompt: {} }), type: 'prompt' }, + }, + async (input: z.infer, { sendChunk }): Promise => { + return await generate(registry, { + ...(await renderOptionsFn(input, undefined)), + onChunk: sendChunk, + }); + } + ) as ExecutablePromptAction; + + const executablePrompt = wrapInExecutablePrompt( + registry, + renderOptionsFn, + rendererAction ); - (a as PromptAction).__config = config; - return a as PromptAction; + + executablePromptAction.__executablePrompt = + executablePrompt as never as ExecutablePrompt>; + rendererAction.__executablePrompt = + executablePrompt as never as ExecutablePrompt>; + + return executablePrompt; } -export type PromptArgument = - | string - | PromptAction; +function wrapInExecutablePrompt< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, +>( + registry: Registry, + renderOptionsFn: ( + input: z.infer, + renderOptions: PromptGenerateOptions | undefined + ) => Promise, + rendererAction: PromptAction +) { + const executablePrompt = (async ( + input?: I, + opts?: PromptGenerateOptions + ): Promise>> => { + return generate(registry, { + ...(await renderOptionsFn(input, opts)), + }); + }) as ExecutablePrompt, O, CustomOptions>; -/** - * This veneer renders a `PromptAction` into a `GenerateOptions` object. - * - * @returns A promise of an options object for use with the `generate()` function. - */ -export async function renderPrompt< + executablePrompt.render = async ( + input?: I, + opts?: PromptGenerateOptions + ): Promise> => { + return { + ...(await renderOptionsFn(input, opts)), + } as GenerateOptions; + }; + + executablePrompt.stream = ( + input?: I, + opts?: PromptGenerateOptions + ): GenerateStreamResponse> => { + return generateStream(registry, renderOptionsFn(input, opts)); + }; + + executablePrompt.asTool = async (): Promise> => { + return rendererAction as unknown as ToolAction; + }; + return executablePrompt; +} + +async function renderSystemPrompt< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, +>( + registry: Registry, + session: Session | undefined, + input: z.infer, + messages: MessageData[], + options: PromptConfig, + promptCache: PromptCache +) { + if (typeof options.system === 'function') { + messages.push({ + role: 'system', + content: normalizeParts( + await options.system(input, { state: session?.state }) + ), + }); + } else if (typeof options.system === 'string') { + // memoize compiled prompt + if (!promptCache.system) { + promptCache.system = await registry.dotprompt.compile(options.system); + } + messages.push({ + role: 'system', + content: await renderDotpromptToParts(promptCache.system, input, session), + }); + } else if (options.system) { + messages.push({ + role: 'system', + content: normalizeParts(options.system), + }); + } +} + +async function renderMessages< I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, >( registry: Registry, - params: { - prompt: PromptArgument; - input: z.infer; - docs?: DocumentData[]; - model: ModelArgument; - config?: z.infer; - } -): Promise> { - let prompt: PromptAction; - if (typeof params.prompt === 'string') { - prompt = await registry.lookupAction(`/prompt/${params.prompt}`); + session: Session | undefined, + input: z.infer, + messages: MessageData[], + options: PromptConfig, + renderOptions: PromptGenerateOptions, + promptCache: PromptCache +) { + if (options.messages) { + if (typeof options.messages === 'function') { + messages.push( + ...(await options.messages(input, { + state: session?.state, + history: renderOptions?.messages, + })) + ); + } else if (typeof options.messages === 'string') { + // memoize compiled prompt + if (!promptCache.messages) { + promptCache.messages = await registry.dotprompt.compile( + options.messages + ); + } + const rendered = await promptCache.messages({ + input, + context: { state: session?.state }, + messages: renderOptions?.messages?.map((m) => + Message.parseData(m) + ) as DpMessage[], + }); + messages.push(...rendered.messages); + } else { + messages.push(...options.messages); + } } else { - prompt = params.prompt as PromptAction; - } - const rendered = (await prompt( - params.input - )) as GenerateRequest; - return { - model: params.model, - config: { ...(rendered.config || {}), ...params.config }, - messages: rendered.messages.slice(0, rendered.messages.length - 1), - prompt: rendered.messages[rendered.messages.length - 1].content, - docs: params.docs, - output: { - format: rendered.output?.format, - schema: rendered.output?.schema, - }, - tools: rendered.tools || [], - } as GenerateOptions; + if (renderOptions.messages) { + messages.push(...renderOptions.messages); + } + } + if (renderOptions?.messages) { + // delete messages from opts so that we don't override messages downstream + delete renderOptions.messages; + } +} + +async function renderUserPrompt< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, +>( + registry: Registry, + session: Session | undefined, + input: z.infer, + messages: MessageData[], + options: PromptConfig, + promptCache: PromptCache +) { + if (typeof options.prompt === 'function') { + messages.push({ + role: 'user', + content: normalizeParts( + await options.prompt(input, { state: session?.state }) + ), + }); + } else if (typeof options.prompt === 'string') { + // memoize compiled prompt + if (!promptCache.userPrompt) { + promptCache.userPrompt = await registry.dotprompt.compile(options.prompt); + } + messages.push({ + role: 'user', + content: await renderDotpromptToParts( + promptCache.userPrompt, + input, + session + ), + }); + } else if (options.prompt) { + messages.push({ + role: 'user', + content: normalizeParts(options.prompt), + }); + } +} + +function modelName( + modelArg: ModelArgument | undefined +): string | undefined { + if (modelArg === undefined) { + return undefined; + } + if (typeof modelArg === 'string') { + return modelArg; + } + if ((modelArg as ModelReference).name) { + return (modelArg as ModelReference).name; + } + return (modelArg as ModelAction).__action.name; +} + +function normalizeParts(parts: string | Part | Part[]): Part[] { + if (Array.isArray(parts)) return parts; + if (typeof parts === 'string') { + return [ + { + text: parts, + }, + ]; + } + return [parts as Part]; +} + +async function renderDotpromptToParts( + promptFn: PromptFunction, + input: any, + session?: Session +): Promise { + const renderred = await promptFn({ + input, + context: { state: session?.state }, + }); + if (renderred.messages.length !== 1) { + throw new Error('parts tempate must produce only one message'); + } + return renderred.messages[0].content; } /** @@ -222,3 +559,157 @@ export function isExecutablePrompt(obj: any): boolean { !!(obj as ExecutablePrompt)?.stream ); } + +export async function loadPromptFolder( + registry: Registry, + dir: string = './prompts', + ns: string +): Promise { + const promptsPath = resolve(dir); + if (existsSync(promptsPath)) { + const dirEnts = readdirSync(promptsPath, { + withFileTypes: true, + recursive: true, + }); + for (const dirEnt of dirEnts) { + if (dirEnt.isFile() && dirEnt.name.endsWith('.prompt')) { + if (dirEnt.name.startsWith('_')) { + const partialName = dirEnt.name.substring(1, dirEnt.name.length - 7); + definePartial( + registry, + partialName, + readFileSync(join(dirEnt.path, dirEnt.name), { + encoding: 'utf8', + }) + ); + logger.debug( + `Registered Dotprompt partial "${partialName}" from "${join(dirEnt.path, dirEnt.name)}"` + ); + } else { + // If this prompt is in a subdirectory, we need to include that + // in the namespace to prevent naming conflicts. + let prefix = ''; + if (promptsPath !== dirEnt.path) { + prefix = dirEnt.path + .replace(`${promptsPath}/`, '') + .replace(/\//g, '-'); + } + await loadPrompt(registry, dirEnt.path, dirEnt.name, prefix, ns); + } + } + } + } +} + +export function definePartial( + registry: Registry, + name: string, + source: string +) { + registry.dotprompt.definePartial(name, source); +} + +export function defineHelper( + registry: Registry, + name: string, + fn: Handlebars.HelperDelegate +) { + registry.dotprompt.defineHelper(name, fn); +} + +async function loadPrompt( + registry: Registry, + path: string, + filename: string, + prefix = '', + ns = 'dotprompt' +): Promise { + let name = `${prefix ? `${prefix}-` : ''}${basename(filename, '.prompt')}`; + let variant: string | null = null; + if (name.includes('.')) { + const parts = name.split('.'); + name = parts[0]; + variant = parts[1]; + } + const source = readFileSync(join(path, filename), 'utf8'); + const parsedPrompt = registry.dotprompt.parse(source); + const promptMetadata = await registry.dotprompt.renderMetadata(parsedPrompt); + if (variant) { + promptMetadata.variant = variant; + } + definePrompt(registry, { + name: registryDefinitionKey(name, variant ?? undefined, ns), + model: promptMetadata.model, + config: promptMetadata.config, + tools: promptMetadata.tools, + description: promptMetadata.description, + output: { + jsonSchema: promptMetadata.output?.schema, + format: promptMetadata.output?.format, + }, + input: { + jsonSchema: promptMetadata.input?.schema, + }, + metadata: { + ...promptMetadata.metadata, + type: 'prompt', + prompt: { + ...promptMetadata, + template: source, + }, + }, + maxTurns: promptMetadata.raw?.['maxTurns'], + toolChoice: promptMetadata.raw?.['toolChoice'], + returnToolRequests: promptMetadata.raw?.['returnToolRequests'], + messages: parsedPrompt.template, + }); +} + +export async function prompt< + I = undefined, + O extends z.ZodTypeAny = z.ZodTypeAny, + CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, +>( + registry: Registry, + name: string, + options?: { variant?: string; dir?: string } +): Promise> { + return await lookupPrompt( + registry, + name, + options?.variant + ); +} + +function registryLookupKey(name: string, variant?: string, ns?: string) { + return `/prompt/${registryDefinitionKey(name, variant, ns)}`; +} + +async function lookupPrompt< + I = undefined, + O extends z.ZodTypeAny = z.ZodTypeAny, + CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, +>( + registry: Registry, + name: string, + variant?: string +): Promise> { + let registryPrompt = + (await registry.lookupAction(registryLookupKey(name, variant))) || + (await registry.lookupAction( + registryLookupKey(name, variant, 'dotprompt') + )); + if (registryPrompt) { + return (registryPrompt as PromptAction) + .__executablePrompt as never as ExecutablePrompt; + } + throw new GenkitError({ + status: 'NOT_FOUND', + message: `Prompt ${name + (variant ? ` (variant ${variant})` : '')} not found`, + }); +} + +function registryDefinitionKey(name: string, variant?: string, ns?: string) { + // "ns/prompt.variant" where ns and variant are optional + return `${ns ? `${ns}/` : ''}${name}${variant ? `.${variant}` : ''}`; +} diff --git a/js/ai/src/session.ts b/js/ai/src/session.ts index 2e7f0cf6d..0bddf6231 100644 --- a/js/ai/src/session.ts +++ b/js/ai/src/session.ts @@ -23,6 +23,7 @@ import { GenerateOptions, Message, MessageData, + PromptGenerateOptions, isExecutablePrompt, tagAsPreamble, } from './index.js'; @@ -220,12 +221,7 @@ export class Session { if (preamble) { const renderOptions = options as PromptRenderOptions; requestBase = preamble - .render({ - input: renderOptions?.input, - model: (renderOptions as BaseGenerateOptions)?.model, - config: (renderOptions as BaseGenerateOptions)?.config, - messages: (renderOptions as BaseGenerateOptions)?.messages, - }) + .render(renderOptions?.input, renderOptions as PromptGenerateOptions) .then((rb) => { return { ...rb, diff --git a/js/ai/src/tool.ts b/js/ai/src/tool.ts index 1ae92d1b6..8ab428af8 100644 --- a/js/ai/src/tool.ts +++ b/js/ai/src/tool.ts @@ -127,7 +127,8 @@ export async function lookupToolByName( let tool = (await registry.lookupAction(name)) || (await registry.lookupAction(`/tool/${name}`)) || - (await registry.lookupAction(`/prompt/${name}`)); + (await registry.lookupAction(`/prompt/${name}`)) || + (await registry.lookupAction(`/prompt/dotprompt/${name}`)); if (!tool) { throw new Error(`Tool ${name} not found`); } diff --git a/js/ai/tests/prompt/prompt_test.ts b/js/ai/tests/prompt/prompt_test.ts index db26ee42e..eaa1d975a 100644 --- a/js/ai/tests/prompt/prompt_test.ts +++ b/js/ai/tests/prompt/prompt_test.ts @@ -16,44 +16,610 @@ import { z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; -import * as assert from 'assert'; -import { describe, it } from 'node:test'; -import { definePrompt, renderPrompt } from '../../src/prompt.js'; +import assert from 'node:assert'; +import { beforeEach, describe, it } from 'node:test'; +import { Document } from '../../lib/document'; +import { GenerateOptions } from '../../lib/index'; +import { Session } from '../../lib/session'; +import { ModelAction, defineModel } from '../../src/model.ts'; +import { + PromptConfig, + PromptGenerateOptions, + definePrompt, +} from '../../src/prompt.ts'; +import { defineTool } from '../../src/tool.ts'; describe('prompt', () => { - let registry = new Registry(); - describe('render()', () => { - it('respects output schema in the definition', async () => { - const schema1 = z.object({ - puppyName: z.string({ description: 'A cute name for a puppy' }), - }); - const prompt1 = definePrompt( - registry, - { - name: 'prompt1', - inputSchema: z.string({ description: 'Dog breed' }), - }, - async (breed) => { - return { - messages: [ - { - role: 'user', - content: [{ text: `Pick a name for a ${breed} puppy` }], - }, - ], - output: { - format: 'json', - schema: schema1, - }, - }; - } + let registry; + + beforeEach(() => { + registry = new Registry(); + + defineEchoModel(registry); + defineTool( + registry, + { + name: 'toolA', + description: 'toolA descr', + }, + async () => 'a' + ); + }); + + let basicTests: { + name: string; + prompt: PromptConfig; + input?: any; + inputOptions?: PromptGenerateOptions; + wantTextOutput?: string; + wantRendered?: GenerateOptions; + state?: any; + only?: boolean; + }[] = [ + { + name: 'renders user prompt', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + prompt: 'hello {{name}} ({{@state.name}})', + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: hello foo (bar); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [{ content: [{ text: 'hello foo (bar)' }], role: 'user' }], + model: 'echoModel', + }, + }, + { + name: 'renders user prompt with explicit messages override', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + prompt: 'hello {{name}} ({{@state.name}})', + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { + config: { temperature: 11 }, + messages: [ + { + role: 'user', + content: [{ text: 'hi' }], + }, + { + role: 'model', + content: [{ text: 'bye' }], + }, + ], + }, + wantTextOutput: + 'Echo: hi,bye,hello foo (bar); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [ + { role: 'user', content: [{ text: 'hi' }] }, + { content: [{ text: 'bye' }], role: 'model' }, + { content: [{ text: 'hello foo (bar)' }], role: 'user' }, + ], + model: 'echoModel', + }, + }, + { + name: 'renders messages prompt with explicit messages override', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + messages: 'hello {{name}} ({{@state.name}})', + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { + config: { temperature: 11 }, + messages: [ + { + role: 'user', + content: [{ text: 'hi' }], + }, + { + role: 'model', + content: [{ text: 'bye' }], + }, + ], + }, + wantTextOutput: + 'Echo: hi,bye,hello foo (bar); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [ + { role: 'user', content: [{ text: 'hi' }] }, + { content: [{ text: 'bye' }], role: 'model' }, + { content: [{ text: 'hello foo (bar)' }], role: 'user' }, + ], + model: 'echoModel', + }, + }, + { + name: 'renders messages {{history}} prompt with explicit messages override', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + messages: 'hello {{name}} ({{@state.name}}){{history}}', + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { + config: { temperature: 11 }, + messages: [ + { + role: 'user', + content: [{ text: 'hi' }], + }, + { + role: 'model', + content: [{ text: 'bye' }], + }, + ], + }, + wantTextOutput: + 'Echo: hello foo (bar),hi,bye; config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [ + { content: [{ text: 'hello foo (bar)' }], role: 'user' }, + { + role: 'user', + content: [{ text: 'hi' }], + metadata: { purpose: 'history' }, + }, + { + content: [{ text: 'bye' }], + role: 'model', + metadata: { purpose: 'history' }, + }, + ], + model: 'echoModel', + }, + }, + { + name: 'renders user prompt from function', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + prompt: async (input, { state }) => { + return `hello ${input.name} (${state.name})`; + }, + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: hello foo (bar); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [{ content: [{ text: 'hello foo (bar)' }], role: 'user' }], + model: 'echoModel', + }, + }, + { + name: 'renders system prompt', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + system: 'hello {{name}} ({{@state.name}})', + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: system: hello foo (bar); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [{ content: [{ text: 'hello foo (bar)' }], role: 'system' }], + model: 'echoModel', + }, + }, + { + name: 'renders system prompt from a function', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + system: async (input, { state }) => { + return `hello ${input.name} (${state.name})`; + }, + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: system: hello foo (bar); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [{ content: [{ text: 'hello foo (bar)' }], role: 'system' }], + model: 'echoModel', + }, + }, + { + name: 'renders messages from template', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + messages: + '{{role "system"}}system {{name}}{{role "user"}}user {{@state.name}}', + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: system: system foo,user bar; config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [ + { role: 'system', content: [{ text: 'system foo' }] }, + { role: 'user', content: [{ text: 'user bar' }] }, + ], + model: 'echoModel', + }, + }, + { + name: 'renders messages', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + messages: [ + { role: 'system', content: [{ text: 'system instructions' }] }, + { role: 'user', content: [{ text: 'user instructions' }] }, + ], + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: system: system instructions,user instructions; config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [ + { role: 'system', content: [{ text: 'system instructions' }] }, + { role: 'user', content: [{ text: 'user instructions' }] }, + ], + model: 'echoModel', + }, + }, + { + name: 'renders messages from function', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + messages: async (input, { state }) => [ + { role: 'system', content: [{ text: `system ${input.name}` }] }, + { role: 'user', content: [{ text: `user ${state.name}` }] }, + ], + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: system: system foo,user bar; config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [ + { role: 'system', content: [{ text: 'system foo' }] }, + { role: 'user', content: [{ text: 'user bar' }] }, + ], + model: 'echoModel', + }, + }, + { + name: 'renders system, message and prompt in the same order', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + prompt: 'hi {{@state.name}}', + system: 'hi {{name}}', + messages: [ + { role: 'user', content: [{ text: `hi` }] }, + { role: 'model', content: [{ text: `bye` }] }, + ], + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: system: hi foo,hi,bye,hi bar; config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [ + { role: 'system', content: [{ text: 'hi foo' }] }, + { role: 'user', content: [{ text: 'hi' }] }, + { role: 'model', content: [{ text: 'bye' }] }, + { role: 'user', content: [{ text: 'hi bar' }] }, + ], + model: 'echoModel', + }, + }, + { + name: 'includes docs', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + prompt: 'hello {{name}} ({{@state.name}})', + docs: [Document.fromText('doc a'), Document.fromText('doc b')], + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: hello foo (bar),\n' + + '\n' + + 'Use the following information to complete your task:\n' + + '\n' + + '- [0]: doc a\n' + + '- [1]: doc b\n' + + '\n' + + '; config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [{ content: [{ text: 'hello foo (bar)' }], role: 'user' }], + model: 'echoModel', + docs: [Document.fromText('doc a'), Document.fromText('doc b')], + }, + }, + { + name: 'includes docs from function', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + prompt: 'hello {{name}} ({{@state.name}})', + docs: async (input, { state }) => [ + Document.fromText(`doc ${input.name}`), + Document.fromText(`doc ${state.name}`), + ], + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: hello foo (bar),\n' + + '\n' + + 'Use the following information to complete your task:\n' + + '\n' + + '- [0]: doc foo\n' + + '- [1]: doc bar\n' + + '\n' + + '; config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [{ content: [{ text: 'hello foo (bar)' }], role: 'user' }], + model: 'echoModel', + docs: [Document.fromText('doc foo'), Document.fromText('doc bar')], + }, + }, + { + name: 'includes tools', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + prompt: 'hello {{name}} ({{@state.name}})', + tools: ['toolA'], + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: hello foo (bar); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [{ content: [{ text: 'hello foo (bar)' }], role: 'user' }], + model: 'echoModel', + tools: ['toolA'], + }, + }, + ]; + + basicTests = basicTests.find((t) => t.only) + ? basicTests.filter((t) => t.only) + : basicTests; + + for (const test of basicTests) { + it(test.name, async () => { + let session: Session | undefined; + if (test.state) { + session = new Session(registry, { + id: '123', + sessionData: { id: '123', state: test.state }, + }); + } + const p = definePrompt(registry, test.prompt); + + const { text } = await (session + ? session.run(() => p(test.input, test.inputOptions)) + : p(test.input, test.inputOptions)); + + assert.strictEqual(text, test.wantTextOutput); + assert.deepStrictEqual( + stripUndefined( + await (session + ? session.run(() => p.render(test.input, test.inputOptions)) + : p.render(test.input, test.inputOptions)) + ), + test.wantRendered ); - const generateRequest = await renderPrompt(registry, { - prompt: prompt1, - input: 'poodle', - model: 'geminiPro', - }); - assert.equal(generateRequest.output?.schema, schema1); }); + } + + it.skip('respects output schema in the definition', async () => { + const schema1 = z.object({ + puppyName: z.string({ description: 'A cute name for a puppy' }), + }); + const prompt1 = definePrompt(registry, { + name: 'prompt1', + input: { schema: z.string({ description: 'Dog breed' }) }, + output: { + format: 'json', + schema: schema1, + }, + messages: (breed) => { + return [ + { + role: 'user', + content: [{ text: `Pick a name for a ${breed} puppy` }], + }, + ]; + }, + }); + const generateRequest = await prompt1.render('poodle', { + model: 'geminiPro', + }); + assert.equal(generateRequest.output?.schema, schema1); }); }); + +export function defineEchoModel(registry: Registry): ModelAction { + const model = defineModel( + registry, + { + name: 'echoModel', + }, + async (request, streamingCallback) => { + (model as any).__test__lastRequest = request; + (model as any).__test__lastStreamingCallback = streamingCallback; + if (streamingCallback) { + streamingCallback({ + content: [ + { + text: '3', + }, + ], + }); + streamingCallback({ + content: [ + { + text: '2', + }, + ], + }); + streamingCallback({ + content: [ + { + text: '1', + }, + ], + }); + } + return { + message: { + role: 'model', + content: [ + { + text: + 'Echo: ' + + request.messages + .map( + (m) => + (m.role === 'user' || m.role === 'model' + ? '' + : `${m.role}: `) + m.content.map((c) => c.text).join() + ) + .join(), + }, + { + text: '; config: ' + JSON.stringify(request.config), + }, + ], + }, + finishReason: 'stop', + }; + } + ); + return model; +} + +function stripUndefined(input: any) { + if ( + input === undefined || + Array.isArray(input) || + typeof input !== 'object' + ) { + return input; + } + const out = {}; + for (const key in input) { + if (input[key] !== undefined) { + out[key] = stripUndefined(input[key]); + } + } + return out; +} diff --git a/js/core/package.json b/js/core/package.json index d0b2327bb..ce78aebfd 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -42,7 +42,8 @@ "get-port": "^5.1.0", "json-schema": "^0.4.0", "zod": "^3.23.8", - "zod-to-json-schema": "^3.22.4" + "zod-to-json-schema": "^3.22.4", + "dotprompt": "^1.0.0-dev || ^1" }, "devDependencies": { "@types/express": "^4.17.21", diff --git a/js/core/src/registry.ts b/js/core/src/registry.ts index 2127453f7..bdf5462d1 100644 --- a/js/core/src/registry.ts +++ b/js/core/src/registry.ts @@ -14,12 +14,14 @@ * limitations under the License. */ +import { DotpromptEnvironment } from 'dotprompt'; import { AsyncLocalStorage } from 'node:async_hooks'; import * as z from 'zod'; import { Action } from './action.js'; +import { GenkitError } from './error.js'; import { logger } from './logging.js'; import { PluginProvider } from './plugin.js'; -import { JSONSchema } from './schema.js'; +import { JSONSchema, toJsonSchema } from './schema.js'; export type AsyncProvider = () => Promise; @@ -35,6 +37,7 @@ export type ActionType = | 'flow' | 'model' | 'prompt' + | 'executable-prompt' | 'util' | 'tool' | 'reranker'; @@ -68,6 +71,18 @@ export class Registry { private allPluginsInitialized = false; readonly asyncStore = new AsyncStore(); + readonly dotprompt = new DotpromptEnvironment({ + schemaResolver: async (name) => { + const resolvedSchema = await this.lookupSchema(name); + if (!resolvedSchema) { + throw new GenkitError({ + message: `Schema '${name}' not found`, + status: 'NOT_FOUND', + }); + } + return toJsonSchema(resolvedSchema); + }, + }); constructor(public parent?: Registry) {} diff --git a/js/core/src/utils.ts b/js/core/src/utils.ts index 7bbde519f..292b10320 100644 --- a/js/core/src/utils.ts +++ b/js/core/src/utils.ts @@ -30,6 +30,26 @@ export function deleteUndefinedProps(obj: any) { } } +/** + * Strips (non distructively) any properties with `undefined` values in the provided object and returns + */ +export function stripUndefinedProps(input: T): T { + if ( + input === undefined || + Array.isArray(input) || + typeof input !== 'object' + ) { + return input; + } + const out = {} as T; + for (const key in input) { + if (input[key] !== undefined) { + out[key] = stripUndefinedProps(input[key]); + } + } + return out; +} + /** * Returns the current environment that the app code is running in. * diff --git a/js/doc-snippets/src/dotprompt/index.ts b/js/doc-snippets/src/dotprompt/index.ts index 2886a8e95..5a4ac9e94 100644 --- a/js/doc-snippets/src/dotprompt/index.ts +++ b/js/doc-snippets/src/dotprompt/index.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { GenerateRequest, genkit, loadPromptFile } from 'genkit'; +import { genkit } from 'genkit'; // [START promptDir] const ai = genkit({ @@ -151,49 +151,37 @@ async function fn08() { function fn09() { // [START definePromptTempl] - const myPrompt = ai.definePrompt( - { - name: 'myPrompt', - model: 'googleai/gemini-1.5-flash', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const myPrompt = ai.definePrompt({ + name: 'myPrompt', + model: 'googleai/gemini-1.5-flash', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'Hello, {{name}}. How are you today?' - ); + prompt: 'Hello, {{name}}. How are you today?', + }); // [END definePromptTempl] } function fn10() { // [START definePromptFn] - const myPrompt = ai.definePrompt( - { - name: 'myPrompt', - model: 'googleai/gemini-1.5-flash', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const myPrompt = ai.definePrompt({ + name: 'myPrompt', + model: 'googleai/gemini-1.5-flash', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input): Promise => { - return { - messages: [ - { - role: 'user', - content: [{ text: `Hello, ${input.name}. How are you today?` }], - }, - ], - }; - } - ); + messages: async (input) => { + return [ + { + role: 'user', + content: [{ text: `Hello, ${input.name}. How are you today?` }], + }, + ]; + }, + }); // [END definePromptFn] } - -async function fn01() { - // [START loadPromptFile] - const helloPrompt = loadPromptFile(ai.registry, './llm_prompts/hello.prompt'); - // [END loadPromptFile] -} diff --git a/js/doc-snippets/src/multi-agent/multi.ts b/js/doc-snippets/src/multi-agent/multi.ts index 6af49bc3b..b995ad803 100644 --- a/js/doc-snippets/src/multi-agent/multi.ts +++ b/js/doc-snippets/src/multi-agent/multi.ts @@ -29,31 +29,27 @@ const reservationListTool = reservationTool; // [START agents] // Define a prompt that represents a specialist agent -const reservationAgent = ai.definePrompt( - { - name: 'reservationAgent', - description: 'Reservation Agent can help manage guest reservations', - tools: [reservationTool, reservationCancelationTool, reservationListTool], - }, - '{{role "system"}} Help guests make and manage reservations' -); +const reservationAgent = ai.definePrompt({ + name: 'reservationAgent', + description: 'Reservation Agent can help manage guest reservations', + tools: [reservationTool, reservationCancelationTool, reservationListTool], + system: 'Help guests make and manage reservations', +}); // Or load agents from .prompt files const menuInfoAgent = ai.prompt('menuInfoAgent'); const complaintAgent = ai.prompt('complaintAgent'); // The triage agent is the agent that users interact with initially -const triageAgent = ai.definePrompt( - { - name: 'triageAgent', - description: 'Triage Agent', - tools: [reservationAgent, menuInfoAgent, complaintAgent], - }, - `{{role "system"}} You are an AI customer service agent for Pavel's Cafe. +const triageAgent = ai.definePrompt({ + name: 'triageAgent', + description: 'Triage Agent', + tools: [reservationAgent, menuInfoAgent, complaintAgent], + system: `You are an AI customer service agent for Pavel's Cafe. Greet the user and ask them how you can help. If appropriate, transfer to an agent that can better handle the request. If you cannot help the customer with - the available tools, politely explain so.` -); + the available tools, politely explain so.`, +}); // [END agents] // [START chat] diff --git a/js/genkit/package.json b/js/genkit/package.json index e74abfe11..620b7f528 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -29,7 +29,6 @@ "dependencies": { "@genkit-ai/core": "workspace:*", "@genkit-ai/ai": "workspace:*", - "@genkit-ai/dotprompt": "workspace:*", "uuid": "^10.0.0" }, "devDependencies": { diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index 82da56a41..1f1f03b41 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -16,128 +16,117 @@ import { BaseDataPointSchema, - defineInterrupt, - definePrompt, - defineTool, Document, - embed, EmbedderInfo, EmbedderParams, Embedding, EvalResponses, - evaluate, EvaluatorParams, ExecutablePrompt, - generate, GenerateOptions, GenerateRequest, GenerateResponse, GenerateResponseData, - generateStream, GenerateStreamOptions, GenerateStreamResponse, GenerationCommonConfigSchema, IndexerParams, InterruptConfig, - isExecutablePrompt, ModelArgument, - ModelReference, Part, - PromptAction, - PromptFn, + PromptConfig, PromptGenerateOptions, RankedDocument, - rerank, RerankerParams, - retrieve, RetrieverAction, RetrieverInfo, RetrieverParams, ToolAction, ToolConfig, + defineHelper, + defineInterrupt, + definePartial, + definePrompt, + defineTool, + embed, + evaluate, + generate, + generateStream, + isExecutablePrompt, + loadPromptFolder, + prompt, + rerank, + retrieve, } from '@genkit-ai/ai'; import { Chat, ChatOptions } from '@genkit-ai/ai/chat'; import { - defineEmbedder, EmbedderAction, EmbedderArgument, EmbedderFn, EmbeddingBatch, + defineEmbedder, embedMany, } from '@genkit-ai/ai/embedder'; import { - defineEvaluator, EvaluatorAction, EvaluatorFn, + defineEvaluator, } from '@genkit-ai/ai/evaluator'; import { + Formatter, configureFormats, defineFormat, - Formatter, } from '@genkit-ai/ai/formats'; import { - defineModel, DefineModelOptions, GenerateResponseChunkData, ModelAction, + defineModel, } from '@genkit-ai/ai/model'; import { - defineReranker, RerankerFn, RerankerInfo, + defineReranker, } from '@genkit-ai/ai/reranker'; import { - defineIndexer, - defineRetriever, - defineSimpleRetriever, DocumentData, - index, IndexerAction, IndexerFn, RetrieverFn, SimpleRetrieverOptions, + defineIndexer, + defineRetriever, + defineSimpleRetriever, + index, } from '@genkit-ai/ai/retriever'; import { - getCurrentSession, Session, SessionData, SessionError, SessionOptions, + getCurrentSession, } from '@genkit-ai/ai/session'; -import { resolveTools, ToolFn } from '@genkit-ai/ai/tool'; +import { ToolFn } from '@genkit-ai/ai/tool'; import { Action, - defineFlow, - defineJsonSchema, - defineSchema, FlowConfig, FlowFn, - isDevEnv, JSONSchema, ReflectionServer, - run, StreamingCallback, + defineFlow, + defineJsonSchema, + defineSchema, + isDevEnv, + run, z, } from '@genkit-ai/core'; import { HasRegistry } from '@genkit-ai/core/registry'; -import { - defineDotprompt, - defineHelper, - definePartial, - Dotprompt, - PromptMetadata as DotpromptPromptMetadata, - loadPromptFolder, - prompt, - toFrontmatter, - type DotpromptAction, -} from '@genkit-ai/dotprompt'; import { v4 as uuidv4 } from 'uuid'; import { BaseEvalDataPointSchema } from './evaluator.js'; import { logger } from './logging.js'; import { GenkitPlugin, genkitPlugin } from './plugin.js'; import { Registry } from './registry.js'; -import { toJsonSchema } from './schema.js'; -import { toToolDefinition } from './tool.js'; /** * Options for initializing Genkit. @@ -151,17 +140,6 @@ export interface GenkitOptions { model?: ModelArgument; } -/** - * Metadata for a prompt. - */ -export type PromptMetadata< - Input extends z.ZodTypeAny = z.ZodTypeAny, - Options extends z.ZodTypeAny = z.ZodTypeAny, -> = Omit, 'name'> & { - /** The name of the prompt. */ - name: string; -}; - /** * `Genkit` encapsulates a single Genkit instance including the {@link Registry}, {@link ReflectionServer}, {@link FlowServer}, and configuration. * @@ -323,32 +301,55 @@ export class Genkit implements HasRegistry { name: string, options?: { variant?: string } ): ExecutablePrompt, O, CustomOptions> { - const actionPromise = (async () => { - // check the registry first as not all prompt types can be - // loaded by dotprompt (e.g. functional) - let action = (await this.registry.lookupAction( - `/prompt/${name}${options?.variant ? `.${options?.variant}` : ''}` - )) as PromptAction; - // nothing in registry - check for dotprompt file. - if (!action) { - action = ( - await prompt(this.registry, name, { - ...options, - dir: this.options.promptDir ?? './prompts', - }) - ).promptAction as PromptAction; + return this.wrapExecutablePromptPromise( + prompt(this.registry, name, { + ...options, + dir: this.options.promptDir ?? './prompts', + }) + ); + } + + private wrapExecutablePromptPromise< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, + >(promise: Promise, O, CustomOptions>>) { + const executablePrompt = (async ( + input?: I, + opts?: PromptGenerateOptions + ): Promise>> => { + return (await promise)(input, opts); + }) as ExecutablePrompt, O, CustomOptions>; + + executablePrompt.render = async ( + opt: PromptGenerateOptions & { + input?: I; } - const { template, ...opts } = action.__action.metadata!.prompt; - return { action, opts }; - })(); + ): Promise> => { + return (await promise).render(opt.input, opt) as Promise< + GenerateOptions + >; + }; - // make sure we get configuration such as model name if applicable - return this.wrapPromptActionInExecutablePrompt( - actionPromise.then(({ action }) => action), - actionPromise.then(({ opts }) => opts) - ) as ExecutablePrompt; - } + executablePrompt.stream = ( + input?: I, + opts?: PromptGenerateOptions + ): GenerateStreamResponse => { + return this.generateStream( + promise.then((action) => + action.render(input, { + ...opts, + }) + ) + ); + }; + + executablePrompt.asTool = async (): Promise> => { + return (await promise).asTool() as Promise>; + }; + return executablePrompt; + } /** * Defines and registers a prompt based on a function. * @@ -388,223 +389,9 @@ export class Genkit implements HasRegistry { O extends z.ZodTypeAny = z.ZodTypeAny, CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, >( - options: PromptMetadata, - fn: PromptFn - ): ExecutablePrompt, O, CustomOptions>; - - /** - * Defines and registers a prompt based on a template. - * - * This is an alternative to defining and importing a .prompt file, in - * situations where a static definition will not suffice. - * - * @param options - The first input number - * @param fn - The second input number - * - * ```ts - * const hi = ai.definePrompt( - * { - * name: 'hi', - * input: { - * schema: z.object({ - * name: z.string(), - * }), - * }, - * }, - * 'hi {{ name }}' - * ); - * const { text } = await hi({ name: 'Genkit' }); - * ``` - */ - definePrompt< - I extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, - >( - options: PromptMetadata, - template: string - ): ExecutablePrompt, O, CustomOptions>; - - definePrompt< - I extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, - >( - options: PromptMetadata, - templateOrFn: string | PromptFn + options: PromptConfig ): ExecutablePrompt, O, CustomOptions> { - if (!options.name) { - throw new Error('options.name is required'); - } - if (!options.name) { - throw new Error('options.name is required'); - } - if (typeof templateOrFn === 'string') { - const dotprompt = defineDotprompt( - this.registry, - { - ...options, - tools: options.tools, - }, - templateOrFn as string - ); - return this.wrapPromptActionInExecutablePrompt( - dotprompt.promptAction! as PromptAction, - options - ); - } else { - const p = definePrompt( - this.registry, - { - ...options, - name: options.name!, - description: options.description, - inputJsonSchema: options.input?.jsonSchema, - inputSchema: options.input?.schema, - metadata: { - type: 'prompt', - // TODO: As a stop-gap, we are using the dotprompt interpretation of - // the "prompt metadata", which is roughly the same as the dotprompt - // frontmatter schema. This should be inverted, such that Genkit - // defines the metadata spec and registered dotprompts conform. - prompt: toFrontmatter(options), - }, - }, - async (input: z.infer) => { - const response = await (templateOrFn as PromptFn)(input); - if (!response.tools && options.tools) { - response.tools = ( - await resolveTools(this.registry, options.tools) - ).map((t) => toToolDefinition(t)); - } - if (!response.output && options.output) { - response.output = { - schema: toJsonSchema({ - schema: options.output.schema, - jsonSchema: options.output.jsonSchema, - }), - }; - } - return response; - } - ); - return this.wrapPromptActionInExecutablePrompt(p, options); - } - } - - private wrapPromptActionInExecutablePrompt< - I extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, - >( - promptAction: PromptAction | Promise>, - options: - | Partial> - | Promise>> - ): ExecutablePrompt { - const executablePrompt = async ( - input?: z.infer, - opts?: PromptGenerateOptions - ): Promise => { - const renderedOpts = await ( - executablePrompt as ExecutablePrompt - ).render({ - ...opts, - input, - }); - return this.generate(renderedOpts); - }; - (executablePrompt as ExecutablePrompt).stream = async ( - input?: z.infer, - opts?: z.infer - ): Promise> => { - const renderedOpts = await ( - executablePrompt as ExecutablePrompt - ).render({ - ...opts, - input, - }); - return this.generateStream(renderedOpts); - }; - (executablePrompt as ExecutablePrompt).render = async ( - opt: PromptGenerateOptions & { - input?: I; - } - ): Promise> => { - let model: ModelAction | undefined; - options = await options; - const modelArg = opt?.model ?? options.model; - if (modelArg) { - model = await this.resolveModel(modelArg); - // If model was explicitly specified and we failed to resolve it (bad ref maybe?), throw an error! - if (!model) { - throw new Error(`Model ${modelArg} not found`); - } - } - const p = await promptAction; - // If it's a dotprompt template, we invoke dotprompt template directly - // because it can take in more PromptGenerateOptions (not just inputs). - const dotprompt: Dotprompt> | undefined = ( - p as DotpromptAction> - ).__dotprompt; - const promptResult = await (dotprompt - ? dotprompt.render(opt) - : p(opt.input)); - const resultOptions = { - messages: promptResult.messages, - docs: promptResult.docs, - tools: promptResult.tools ?? options.tools, - output: - promptResult.output?.format || promptResult.output?.schema - ? { - format: promptResult.output?.format, - jsonSchema: dotprompt - ? (promptResult as GenerateOptions).output?.jsonSchema - : promptResult.output.schema, - contentType: promptResult.output?.contentType, - instructions: promptResult.output?.instructions, - schema: promptResult.output?.schema, - } - : options.output, - config: { - ...options.config, - ...promptResult.config, - ...opt.config, - }, - model, - } as GenerateOptions; - if ((promptResult as GenerateOptions).use) { - resultOptions.use = (promptResult as GenerateOptions).use; - } else if (p.__config?.use) { - resultOptions.use = p.__config?.use; - } else if (opt.use) { - resultOptions.use = opt.use; - } - if ((promptResult as GenerateOptions).tools) { - resultOptions.tools = (promptResult as GenerateOptions).tools; - } else if (p.__config?.tools) { - resultOptions.tools = p.__config?.tools; - } else if (opt.tools) { - resultOptions.tools = opt.tools; - } - if ((promptResult as GenerateOptions).toolChoice) { - resultOptions.toolChoice = (promptResult as GenerateOptions).toolChoice; - } else if (p.__config?.toolChoice) { - resultOptions.toolChoice = p.__config?.toolChoice; - } else if (opt.toolChoice) { - resultOptions.toolChoice = opt.toolChoice; - } - delete (resultOptions as any).input; - if ((promptResult as GenerateOptions).prompt) { - resultOptions.prompt = (promptResult as GenerateOptions).prompt; - } - return resultOptions; - }; - (executablePrompt as ExecutablePrompt).asTool = - async (): Promise> => { - return (await promptAction) as unknown as ToolAction; - }; - return executablePrompt as ExecutablePrompt; + return definePrompt(this.registry, options); } /** @@ -689,15 +476,15 @@ export class Genkit implements HasRegistry { /** * create a handlebards helper (https://handlebarsjs.com/guide/block-helpers.html) to be used in dotpormpt templates. */ - defineHelper(name: string, fn: Handlebars.HelperDelegate) { - return defineHelper(name, fn); + defineHelper(name: string, fn: Handlebars.HelperDelegate): void { + defineHelper(this.registry, name, fn); } /** * Creates a handlebars partial (https://handlebarsjs.com/guide/partials.html) to be used in dotpormpt templates. */ - definePartial(name: string, source: string) { - return definePartial(name, source); + definePartial(name: string, source: string): void { + definePartial(this.registry, name, source); } /** @@ -1133,10 +920,10 @@ export class Genkit implements HasRegistry { } if (this.options.promptDir !== null) { const dotprompt = genkitPlugin('dotprompt', async (ai) => { - loadPromptFolder( + await loadPromptFolder( this.registry, this.options.promptDir ?? './prompts', - '' + 'dotprompt' ); }); plugins.push(dotprompt); @@ -1161,29 +948,6 @@ export class Genkit implements HasRegistry { await this.reflectionServer?.stop(); this.reflectionServer = null; } - - private async resolveModel( - modelArg: ModelArgument | undefined - ): Promise { - if (!modelArg) { - if (!this.options.model) { - throw new Error('Unable to resolve model.'); - } - return this.resolveModel(this.options.model); - } - if (typeof modelArg === 'string') { - return (await this.registry.lookupAction( - `/model/${modelArg}` - )) as ModelAction; - } else if ((modelArg as ModelAction).__action) { - return modelArg as ModelAction; - } else { - const ref = modelArg as ModelReference; - return (await this.registry.lookupAction( - `/model/${ref.name}` - )) as ModelAction; - } - } } /** diff --git a/js/genkit/src/index.ts b/js/genkit/src/index.ts index cbbef6c05..e19acb2e7 100644 --- a/js/genkit/src/index.ts +++ b/js/genkit/src/index.ts @@ -84,7 +84,6 @@ export { type Part, type PromptAction, type PromptConfig, - type PromptFn, type RankedDocument, type RerankerAction, type RerankerArgument, @@ -141,10 +140,4 @@ export { type StreamingResponse, type TelemetryConfig, } from '@genkit-ai/core'; -export { loadPromptFile } from '@genkit-ai/dotprompt'; -export { - Genkit, - genkit, - type GenkitOptions, - type PromptMetadata, -} from './genkit.js'; +export { Genkit, genkit, type GenkitOptions } from './genkit.js'; diff --git a/js/genkit/tests/chat_test.ts b/js/genkit/tests/chat_test.ts index 988b8a21c..2be245354 100644 --- a/js/genkit/tests/chat_test.ts +++ b/js/genkit/tests/chat_test.ts @@ -105,13 +105,15 @@ describe('chat', () => { }); it('can init a session with a prompt', async () => { - const prompt = ai.definePrompt({ name: 'hi' }, 'hi {{ name }}'); + const prompt = ai.definePrompt({ name: 'hi', prompt: 'hi {{ name }}' }); const session = await ai.chat( - await prompt.render({ - input: { name: 'Genkit' }, - config: { temperature: 11 }, - }) + await prompt.render( + { name: 'Genkit' }, + { + config: { temperature: 11 }, + } + ) ); const response = await session.send('hi'); @@ -122,10 +124,11 @@ describe('chat', () => { }); it('can start chat from a prompt', async () => { - const preamble = ai.definePrompt( - { name: 'hi', config: { version: 'abc' } }, - 'hi from template' - ); + const preamble = ai.definePrompt({ + name: 'hi', + config: { version: 'abc' }, + messages: 'hi from template', + }); const session = await ai.chat(preamble); const response = await session.send('send it'); @@ -136,10 +139,11 @@ describe('chat', () => { }); it('can start chat from a prompt with input', async () => { - const preamble = ai.definePrompt( - { name: 'hi', config: { version: 'abc' } }, - 'hi {{ name }} from template' - ); + const preamble = ai.definePrompt({ + name: 'hi', + config: { version: 'abc' }, + messages: 'hi {{ name }} from template', + }); const session = await ai.chat(preamble, { input: { name: 'Genkit' }, }); @@ -152,16 +156,19 @@ describe('chat', () => { }); it('can send a rendered prompt to chat', async () => { - const prompt = ai.definePrompt( - { name: 'hi', config: { version: 'abc' } }, - 'hi {{ name }}' - ); + const prompt = ai.definePrompt({ + name: 'hi', + config: { version: 'abc' }, + prompt: 'hi {{ name }}', + }); const session = ai.chat(); const response = await session.send( - await prompt.render({ - input: { name: 'Genkit' }, - config: { temperature: 11 }, - }) + await prompt.render( + { name: 'Genkit' }, + { + config: { temperature: 11 }, + } + ) ); assert.strictEqual( @@ -184,36 +191,30 @@ describe('preamble', () => { }); it('swaps out preamble on prompt tool invocation', async () => { - const agentB = ai.definePrompt( - { - name: 'agentB', - config: { temperature: 1 }, - description: 'Agent B description', - tools: ['agentA'], - toolChoice: 'required', - }, - '{{role "system"}} agent b' - ); + const agentB = ai.definePrompt({ + name: 'agentB', + config: { temperature: 1 }, + description: 'Agent B description', + tools: ['agentA'], + toolChoice: 'required', + system: 'agent b', + }); - const agentA = ai.definePrompt( - { - name: 'agentA', - config: { temperature: 2 }, - description: 'Agent A description', - tools: [agentB], - toolChoice: 'required', - }, - async () => { - return { - messages: [ - { - role: 'system', - content: [{ text: ' agent a' }], - }, - ], - }; - } - ); + const agentA = ai.definePrompt({ + name: 'agentA', + config: { temperature: 2 }, + description: 'Agent A description', + tools: [agentB], + toolChoice: 'required', + messages: async () => { + return [ + { + role: 'system', + content: [{ text: ' agent a' }], + }, + ]; + }, + }); // simple hi, nothing interesting... pm.handleResponse = async (req, sc) => { @@ -295,7 +296,7 @@ describe('preamble', () => { messages: [ { role: 'system', - content: [{ text: ' agent b' }], // <--- NOTE: swapped out the preamble + content: [{ text: 'agent b' }], // <--- NOTE: swapped out the preamble metadata: { preamble: true }, }, { @@ -474,14 +475,12 @@ describe('preamble', () => { }); it('updates the preamble on fresh chat instance', async () => { - const agent = ai.definePrompt( - { - name: 'agent', - config: { temperature: 2 }, - description: 'Agent A description', - }, - '{{ role "system"}} greet {{ @state.name }}' - ); + const agent = ai.definePrompt({ + name: 'agent', + config: { temperature: 2 }, + description: 'Agent A description', + messages: '{{ role "system"}} greet {{ @state.name }}', + }); const session = ai.createSession({ initialState: { name: 'Pavel' } }); @@ -595,18 +594,17 @@ describe('preamble', () => { }); it('initializes chat with history in preamble', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - model: 'echoModel', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const hi = ai.definePrompt({ + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), }, - '{{ role "system"}}system instructions{{ history}}hi {{ name }}' - ); + system: 'system instructions', + prompt: 'hi {{ name }}', + }); const history: MessageData[] = [ { @@ -633,7 +631,6 @@ describe('preamble', () => { content: [{ text: 'hi' }], metadata: { preamble: true, - purpose: 'history', }, }, { @@ -641,11 +638,10 @@ describe('preamble', () => { content: [{ text: 'bye' }], metadata: { preamble: true, - purpose: 'history', }, }, { - role: 'model', + role: 'user', content: [{ text: 'hi Genkit' }], metadata: { preamble: true, diff --git a/js/genkit/tests/prompts/kitchensink.prompt b/js/genkit/tests/prompts/kitchensink.prompt new file mode 100644 index 000000000..b494a8c12 --- /dev/null +++ b/js/genkit/tests/prompts/kitchensink.prompt @@ -0,0 +1,25 @@ +--- +model: googleai/gemini-5.0-ultimate-pro-plus +description: a description +config: + temperature: 11 +tools: + - toolA + - toolB +returnToolRequests: true +input: + schema: + subject: string +output: + format: csv + schema: + obj?(object, a nested object): + nest1?: string + arr(array, array of objects): + nest2?: boolean +maxTurns: 77 +toolChoice: required +metadata: + foo: bar +--- +{{role "system"}} Hello {{history}} from the prompt file {{ subject }} \ No newline at end of file diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index a85283eac..aca2c4232 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -72,26 +72,22 @@ describe('definePrompt - functional', () => { }); it('should apply middleware to a prompt call', async () => { - const prompt = ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const prompt = ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [ - { - role: 'user', - content: [{ text: `hi ${input.name}` }], - }, - ], - }; - } - ); + messages: async (input) => { + return [ + { + role: 'user', + content: [{ text: `hi ${input.name}` }], + }, + ]; + }, + }); const response = await prompt( { name: 'Genkit' }, @@ -100,55 +96,47 @@ describe('definePrompt - functional', () => { assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]'); }); - it.only('should apply middleware configured on a prompt', async () => { - const prompt = ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, - use: [wrapRequest, wrapResponse], + it('should apply middleware configured on a prompt', async () => { + const prompt = ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [ - { - role: 'user', - content: [{ text: `hi ${input.name}` }], - }, - ], - }; - } - ); + use: [wrapRequest, wrapResponse], + messages: async (input) => { + return [ + { + role: 'user', + content: [{ text: `hi ${input.name}` }], + }, + ]; + }, + }); const response = await prompt({ name: 'Genkit' }); assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]'); }); - it.only('should apply middleware to a prompt call on a looked up prompt', async () => { - ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, - use: [wrapRequest, wrapResponse], + it('should apply middleware to a prompt call on a looked up prompt', async () => { + ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [ - { - role: 'user', - content: [{ text: `hi ${input.name}` }], - }, - ], - }; - } - ); + use: [wrapRequest, wrapResponse], + messages: async (input) => { + return [ + { + role: 'user', + content: [{ text: `hi ${input.name}` }], + }, + ]; + }, + }); const prompt = ai.prompt('hi'); @@ -157,26 +145,22 @@ describe('definePrompt - functional', () => { }); it('should apply middleware configured on a prompt on a looked up prompt', async () => { - ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, + ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [ - { - role: 'user', - content: [{ text: `hi ${input.name}` }], - }, - ], - }; - } - ); + messages: async (input) => { + return [ + { + role: 'user', + content: [{ text: `hi ${input.name}` }], + }, + ]; + }, + }); const prompt = ai.prompt('hi'); @@ -200,37 +184,33 @@ describe('definePrompt - dotprompt', () => { }); it('calls dotprompt with default model', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const hi = ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + prompt: 'hi {{ name }}', + }); const response = await hi({ name: 'Genkit' }); assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); }); it('calls dotprompt with default model with config', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, - config: { - temperature: 11, - }, + const hi = ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + config: { + temperature: 11, + }, + prompt: 'hi {{ name }}', + }); const response = await hi({ name: 'Genkit' }); assert.strictEqual( @@ -240,17 +220,15 @@ describe('definePrompt - dotprompt', () => { }); it('calls dotprompt with default model via retrieved prompt', async () => { - ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, + ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + prompt: 'hi {{ name }}', + }); const hi = ai.prompt('hi'); @@ -259,17 +237,15 @@ describe('definePrompt - dotprompt', () => { }); it('should apply middleware to a prompt call', async () => { - const prompt = ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const prompt = ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + prompt: 'hi {{ name }}', + }); const response = await prompt( { name: 'Genkit' }, @@ -279,36 +255,32 @@ describe('definePrompt - dotprompt', () => { }); it('should apply middleware configured on a prompt', async () => { - const prompt = ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, - use: [wrapRequest, wrapResponse], + const prompt = ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + use: [wrapRequest, wrapResponse], + prompt: 'hi {{ name }}', + }); const response = await prompt({ name: 'Genkit' }); assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]'); }); - it.only('should apply middleware to a prompt call on a looked up prompt', async () => { - ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, - use: [wrapRequest, wrapResponse], + it('should apply middleware to a prompt call on a looked up prompt', async () => { + ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + use: [wrapRequest, wrapResponse], + prompt: 'hi {{ name }}', + }); const prompt = ai.prompt('hi'); @@ -316,18 +288,16 @@ describe('definePrompt - dotprompt', () => { assert.strictEqual(response.text, '[Echo: (hi Genkit),; config: {}]'); }); - it.only('should apply middleware configured on a prompt on a looked up prompt', async () => { - ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, + it('should apply middleware configured on a prompt on a looked up prompt', async () => { + ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + prompt: 'hi {{ name }}', + }); const prompt = ai.prompt('hi'); @@ -352,17 +322,15 @@ describe('definePrompt - dotprompt', () => { }); it('calls dotprompt with default model', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const hi = ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + prompt: 'hi {{ name }}', + }); const response = await hi({ name: 'Genkit' }); assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); @@ -380,25 +348,23 @@ describe('definePrompt - dotprompt', () => { }, ], }); - const hi = ai.definePrompt( - { - name: 'hi', - model, - input: { - schema: z.object({ - name: z.string(), - }), - }, - output: { - format: 'json', - schema: Foo, - }, + const hi = ai.definePrompt({ + name: 'hi', + model, + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + output: { + format: 'json', + schema: Foo, + }, + prompt: 'hi {{ name }}', + }); const response = await hi({ name: 'Genkit' }); - const foo: z.infer = response.output; + const foo = response.output; assert.deepStrictEqual(foo, { bar: 'baz' }); }); @@ -414,45 +380,41 @@ describe('definePrompt - dotprompt', () => { }, ], }); - const hi = ai.definePrompt( - { - name: 'hi', - model, - input: { - schema: z.object({ - name: z.string(), - }), - }, - output: { - // no format specified - schema: Foo, - }, + const hi = ai.definePrompt({ + name: 'hi', + model, + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + output: { + // no format specified + schema: Foo, + }, + prompt: 'hi {{ name }}', + }); const response = await hi({ name: 'Genkit' }); - const foo: z.infer = response.output; + const foo = response.output; assert.deepStrictEqual(foo, { bar: 'baz' }); }); it('streams dotprompt with default model', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, - config: { - temperature: 11, - }, + const hi = ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + config: { + temperature: 11, + }, + prompt: 'hi {{ name }}', + }); - const { response, stream } = await hi.stream({ name: 'Genkit' }); + const { response, stream } = hi.stream({ name: 'Genkit' }); const chunks: string[] = []; for await (const chunk of stream) { chunks.push(chunk.text); @@ -467,22 +429,27 @@ describe('definePrompt - dotprompt', () => { }); it('calls dotprompt with default model via retrieved prompt', async () => { - ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, + ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + prompt: 'hi {{ name }}', + }); const hi = ai.prompt('hi'); const response = await hi({ name: 'Genkit' }); assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); + + const { stream } = hi.stream({ name: 'Genkit' }); + const chunks: string[] = []; + for await (const chunk of stream) { + chunks.push(chunk.text); + } + assert.deepStrictEqual(chunks, ['3', '2', '1']); }); }); @@ -495,88 +462,32 @@ describe('definePrompt - dotprompt', () => { }); it('calls dotprompt with default model', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - model: 'echoModel', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const hi = ai.definePrompt({ + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + prompt: 'hi {{ name }}', + }); const response = await hi({ name: 'Genkit' }); assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); }); - it('calls dotprompt with history and places it at {{ history }}', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - model: 'echoModel', - input: { - schema: z.object({ - name: z.string(), - }), - }, - }, - '{{ role "system"}} talk like a pirate {{ history}} hi {{ name }}' - ); - - const response = await hi( - { name: 'Genkit' }, - { - messages: [ - { role: 'user', content: [{ text: 'hi' }] }, - { role: 'model', content: [{ text: 'bye' }] }, - ], - } - ); - assert.deepStrictEqual(response.messages, [ - { - role: 'system', - content: [{ text: ' talk like a pirate ' }], - }, - { - role: 'user', - content: [{ text: 'hi' }], - metadata: { purpose: 'history' }, - }, - { - role: 'model', - content: [{ text: 'bye' }], - metadata: { purpose: 'history' }, - }, - { - role: 'model', - content: [{ text: ' hi Genkit' }], - }, - { - role: 'model', - content: [ - { text: 'Echo: system: talk like a pirate ,hi,bye, hi Genkit' }, - { text: '; config: {}' }, - ], - }, - ]); - }); - it('calls dotprompt with history and places it before user message', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - model: 'echoModel', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const hi = ai.definePrompt({ + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + prompt: 'hi {{ name }}', + }); const response = await hi( { name: 'Genkit' }, @@ -611,18 +522,16 @@ describe('definePrompt - dotprompt', () => { }); it('streams dotprompt with history and places it before user message', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - model: 'echoModel', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const hi = ai.definePrompt({ + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + prompt: 'hi {{ name }}', + }); const response = await hi.stream( { name: 'Genkit' }, @@ -657,21 +566,19 @@ describe('definePrompt - dotprompt', () => { }); it('calls dotprompt with default model with config', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - model: 'echoModel', - input: { - schema: z.object({ - name: z.string(), - }), - }, - config: { - temperature: 11, - }, + const hi = ai.definePrompt({ + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), + }, + config: { + temperature: 11, }, - 'hi {{ name }}' - ); + prompt: 'hi {{ name }}', + }); const response = await hi({ name: 'Genkit' }); assert.strictEqual( @@ -681,22 +588,20 @@ describe('definePrompt - dotprompt', () => { }); it('rejects on invalid model', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - model: 'modelThatDoesNotExist', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const hi = ai.definePrompt({ + name: 'hi', + model: 'modelThatDoesNotExist', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + prompt: 'hi {{ name }}', + }); const response = hi({ name: 'Genkit' }); await assert.rejects(response, { - message: 'Model modelThatDoesNotExist not found', + message: 'NOT_FOUND: Model modelThatDoesNotExist not found', }); }); }); @@ -710,31 +615,21 @@ describe('definePrompt - dotprompt', () => { }); it('renderes dotprompt messages', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const hi = ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - 'hi {{ name }}' - ); + prompt: 'hi {{ name }}', + }); - const response = await hi.render({ input: { name: 'Genkit' } }); + const response = await hi.render({ name: 'Genkit' }); delete response.model; // ignore assert.deepStrictEqual(response, { config: {}, - docs: undefined, - prompt: [ - { - text: 'hi Genkit', - }, - ], - messages: [], - output: undefined, - tools: [], + messages: [{ content: [{ text: 'hi Genkit' }], role: 'user' }], }); }); }); @@ -752,49 +647,37 @@ describe('definePrompt', () => { }); it('calls prompt with default model', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const hi = ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [ - { role: 'user', content: [{ text: `hi ${input.name}` }] }, - ], - }; - } - ); + messages: async (input) => { + return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }]; + }, + }); const response = await hi({ name: 'Genkit' }); assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); }); it('calls prompt with default model with config', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, - config: { - temperature: 11, - }, + const hi = ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [ - { role: 'user', content: [{ text: `hi ${input.name}` }] }, - ], - }; - } - ); + config: { + temperature: 11, + }, + messages: async (input) => { + return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }]; + }, + }); const response = await hi({ name: 'Genkit' }); assert.strictEqual( @@ -804,23 +687,17 @@ describe('definePrompt', () => { }); it('calls prompt with default model via retrieved prompt', async () => { - ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, + ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [ - { role: 'user', content: [{ text: `hi ${input.name}` }] }, - ], - }; - } - ); + messages: async (input) => { + return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }]; + }, + }); const hi = ai.prompt('hi'); @@ -842,49 +719,37 @@ describe('definePrompt', () => { }); it('calls prompt with default model', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const hi = ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [ - { role: 'user', content: [{ text: `hi ${input.name}` }] }, - ], - }; - } - ); + messages: async (input) => { + return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }]; + }, + }); const response = await hi({ name: 'Genkit' }); assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); }); it('streams prompt with default model', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - input: { - schema: z.object({ - name: z.string(), - }), - }, - config: { - temperature: 11, - }, + const hi = ai.definePrompt({ + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [ - { role: 'user', content: [{ text: `hi ${input.name}` }] }, - ], - }; - } - ); + config: { + temperature: 11, + }, + messages: async (input) => { + return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }]; + }, + }); const { response, stream } = await hi.stream({ name: 'Genkit' }); const chunks: string[] = []; @@ -910,51 +775,39 @@ describe('definePrompt', () => { }); it('calls prompt with default model', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - model: 'echoModel', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const hi = ai.definePrompt({ + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [ - { role: 'user', content: [{ text: `hi ${input.name}` }] }, - ], - }; - } - ); + messages: async (input) => { + return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }]; + }, + }); const response = await hi({ name: 'Genkit' }); assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); }); it('calls prompt with default model with config', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - model: 'echoModel', - input: { - schema: z.object({ - name: z.string(), - }), - }, - config: { - temperature: 11, - }, + const hi = ai.definePrompt({ + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [ - { role: 'user', content: [{ text: `hi ${input.name}` }] }, - ], - }; - } - ); + config: { + temperature: 11, + }, + messages: async (input) => { + return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }]; + }, + }); const response = await hi({ name: 'Genkit' }); assert.strictEqual( @@ -964,27 +817,21 @@ describe('definePrompt', () => { }); it('calls prompt with default model with call site config', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - model: 'echoModel', - input: { - schema: z.object({ - name: z.string(), - }), - }, - config: { - temperature: 11, - }, + const hi = ai.definePrompt({ + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [ - { role: 'user', content: [{ text: `hi ${input.name}` }] }, - ], - }; - } - ); + config: { + temperature: 11, + }, + messages: async (input) => { + return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }]; + }, + }); const response = await hi( { name: 'Genkit' }, @@ -1010,30 +857,23 @@ describe('definePrompt', () => { }); it('renders prompt', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - model: 'echoModel', - input: { - schema: z.object({ - name: z.string(), - }), - }, + const hi = ai.definePrompt({ + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [ - { role: 'user', content: [{ text: `hi ${input.name}` }] }, - ], - }; - } - ); + messages: async (input) => { + return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }]; + }, + }); - const response = await hi.render({ input: { name: 'Genkit' } }); + const response = await hi.render({ name: 'Genkit' }); delete response.model; // ignore assert.deepStrictEqual(response, { config: {}, - docs: undefined, messages: [ { content: [ @@ -1044,14 +884,12 @@ describe('definePrompt', () => { role: 'user', }, ], - output: undefined, - tools: undefined, }); }); }); }); -describe('prompt', () => { +describe.only('prompt', () => { let ai: Genkit; let pm: ProgrammableModel; @@ -1075,6 +913,75 @@ describe('prompt', () => { ); }); + it.only('loads from from the folder with all the options', async () => { + const testPrompt = ai.prompt('kitchensink'); // see tests/prompts folder + + const request = await testPrompt.render({ subject: 'banana' }); + + assert.deepStrictEqual(request, { + model: 'googleai/gemini-5.0-ultimate-pro-plus', + config: { + temperature: 11, + }, + output: { + format: 'csv', + jsonSchema: { + additionalProperties: false, + properties: { + arr: { + description: 'array of objects', + items: { + additionalProperties: false, + properties: { + nest2: { + type: ['boolean', 'null'], + }, + }, + type: 'object', + }, + type: 'array', + }, + obj: { + additionalProperties: false, + description: 'a nested object', + properties: { + nest1: { + type: ['string', 'null'], + }, + }, + type: ['object', 'null'], + }, + }, + required: ['arr'], + type: 'object', + }, + }, + maxTurns: 77, + messages: [ + { + content: [ + { + text: ' Hello ', + }, + ], + role: 'system', + }, + { + content: [ + { + text: ' from the prompt file ', + }, + ], + role: 'model', + }, + ], + returnToolRequests: true, + toolChoice: 'required', + subject: 'banana', + tools: ['toolA', 'toolB'], + }); + }); + it('loads a varaint from from the folder', async () => { const testPrompt = ai.prompt('test', { variant: 'variant' }); // see tests/prompts folder @@ -1082,30 +989,26 @@ describe('prompt', () => { assert.strictEqual( text, - 'Echo: Hello from a variant of the hello prompt\n; config: {"temperature":13}' + 'Echo: Hello from a variant of the hello prompt; config: {"temperature":13}' ); }); it('returns a ref to functional prompts', async () => { - ai.definePrompt( - { - name: 'hi', - model: 'echoModel', - input: { - schema: z.object({ - name: z.string(), - }), - }, + ai.definePrompt({ + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [{ role: 'user', content: [{ text: `hi ${input.name}` }] }], - config: { - temperature: 11, - }, - }; - } - ); + config: { + temperature: 11, + }, + messages: async (input) => { + return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }]; + }, + }); const testPrompt = ai.prompt('hi'); const { text } = await testPrompt({ name: 'banana' }); @@ -1113,28 +1016,19 @@ describe('prompt', () => { }); it('includes metadata for functional prompts', async () => { - ai.definePrompt( - { - name: 'hi', - model: 'echoModel', - input: { - schema: z.object({ - name: z.string(), - }), - }, - config: { - temperature: 0.13, - }, + ai.definePrompt({ + name: 'hi', + model: 'echoModel', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [], - config: { - temperature: 11, - }, - }; - } - ); + config: { + temperature: 0.13, + }, + messages: async (input) => [], + }); const testPrompt: PromptAction = await ai.registry.lookupAction('/prompt/hi'); @@ -1164,31 +1058,27 @@ describe('prompt', () => { }); it('passes in output options to the model', async () => { - const hi = ai.definePrompt( - { - name: 'hi', - model: 'programmableModel', - input: { - schema: z.object({ - name: z.string(), - }), - }, - output: { - schema: z.object({ - message: z.string(), - }), - format: 'json', - }, + const hi = ai.definePrompt({ + name: 'hi', + model: 'programmableModel', + input: { + schema: z.object({ + name: z.string(), + }), }, - async (input) => { - return { - messages: [{ role: 'user', content: [{ text: `hi ${input.name}` }] }], - config: { - temperature: 11, - }, - }; - } - ); + output: { + schema: z.object({ + message: z.string(), + }), + format: 'json', + }, + config: { + temperature: 11, + }, + messages: async (input) => { + return [{ role: 'user', content: [{ text: `hi ${input.name}` }] }]; + }, + }); pm.handleResponse = async (req, sc) => { return { @@ -1221,24 +1111,20 @@ describe('asTool', () => { it('swaps out preamble on .prompt file tool invocation', async () => { const session = ai.createSession({ initialState: { name: 'Genkit' } }); - const agentA = ai.definePrompt( - { - name: 'agentA', - config: { temperature: 2 }, - description: 'Agent A description', - tools: ['toolPrompt'], // <--- defined in a .prompt file + const agentA = ai.definePrompt({ + name: 'agentA', + config: { temperature: 2 }, + description: 'Agent A description', + tools: ['toolPrompt'], // <--- defined in a .prompt file + messages: async () => { + return [ + { + role: 'system', + content: [{ text: ' agent a' }], + }, + ]; }, - async () => { - return { - messages: [ - { - role: 'system', - content: [{ text: ' agent a' }], - }, - ], - }; - } - ); + }); // simple hi, nothing interesting... pm.handleResponse = async (req, sc) => { @@ -1278,6 +1164,9 @@ describe('asTool', () => { outputSchema: { $schema: 'http://json-schema.org/draft-07/schema#', }, + metadata: { + originalName: 'dotprompt/toolPrompt', + }, }, ], }); @@ -1487,6 +1376,9 @@ describe('asTool', () => { outputSchema: { $schema: 'http://json-schema.org/draft-07/schema#', }, + metadata: { + originalName: 'dotprompt/toolPrompt', + }, }, ], }); diff --git a/js/genkit/tests/session_test.ts b/js/genkit/tests/session_test.ts index 8bdebe355..8e971747e 100644 --- a/js/genkit/tests/session_test.ts +++ b/js/genkit/tests/session_test.ts @@ -361,14 +361,12 @@ describe('session', () => { }); it('can start chat from a prompt', async () => { - const agent = ai.definePrompt( - { - name: 'agent', - config: { temperature: 1 }, - description: 'Agent description', - }, - '{{role "system"}} hello from template' - ); + const agent = ai.definePrompt({ + name: 'agent', + config: { temperature: 1 }, + description: 'Agent description', + system: 'hello from template', + }); const session = ai.createSession(); const chat = session.chat(agent); @@ -376,7 +374,7 @@ describe('session', () => { assert.deepStrictEqual(respose.messages, [ { role: 'system', - content: [{ text: ' hello from template' }], + content: [{ text: 'hello from template' }], metadata: { preamble: true }, }, { @@ -385,7 +383,7 @@ describe('session', () => { }, { content: [ - { text: 'Echo: system: hello from template,hi' }, + { text: 'Echo: system: hello from template,hi' }, { text: '; config: {"temperature":1}' }, ], role: 'model', @@ -394,14 +392,12 @@ describe('session', () => { }); it('can start chat from a prompt with input', async () => { - const agent = ai.definePrompt( - { - name: 'agent', - config: { temperature: 1 }, - description: 'Agent description', - }, - '{{role "system"}} hello {{ name }} from template' - ); + const agent = ai.definePrompt({ + name: 'agent', + config: { temperature: 1 }, + description: 'Agent description', + system: 'hello {{ name }} from template', + }); const session = ai.createSession(); const chat = session.chat(agent, { @@ -413,7 +409,7 @@ describe('session', () => { assert.deepStrictEqual(respose.messages, [ { role: 'system', - content: [{ text: ' hello Genkit from template' }], + content: [{ text: 'hello Genkit from template' }], metadata: { preamble: true }, }, { @@ -422,7 +418,7 @@ describe('session', () => { }, { content: [ - { text: 'Echo: system: hello Genkit from template,hi' }, + { text: 'Echo: system: hello Genkit from template,hi' }, { text: '; config: {"temperature":1}' }, ], role: 'model', @@ -431,14 +427,12 @@ describe('session', () => { }); it('can start chat thread from a prompt with input', async () => { - const agent = ai.definePrompt( - { - name: 'agent', - config: { temperature: 1 }, - description: 'Agent description', - }, - '{{role "system"}} hello {{ name }} from template' - ); + const agent = ai.definePrompt({ + name: 'agent', + config: { temperature: 1 }, + description: 'Agent description', + system: 'hello {{ name }} from template', + }); const store = new TestMemorySessionStore(); const session = ai.createSession({ store }); const chat = session.chat('mythread', agent, { @@ -455,7 +449,7 @@ describe('session', () => { mythread: [ { role: 'system', - content: [{ text: ' hello Genkit from template' }], + content: [{ text: 'hello Genkit from template' }], metadata: { preamble: true }, }, { @@ -464,7 +458,7 @@ describe('session', () => { }, { content: [ - { text: 'Echo: system: hello Genkit from template,hi' }, + { text: 'Echo: system: hello Genkit from template,hi' }, { text: '; config: {"temperature":1}' }, ], role: 'model', @@ -474,14 +468,12 @@ describe('session', () => { }); it('can read current session state from a prompt', async () => { - const agent = ai.definePrompt( - { - name: 'agent', - config: { temperature: 1 }, - description: 'Agent description', - }, - '{{role "system"}} foo={{@state.foo}}' - ); + const agent = ai.definePrompt({ + name: 'agent', + config: { temperature: 1 }, + description: 'Agent description', + system: 'foo={{@state.foo}}', + }); const session = ai.createSession({ initialState: { @@ -493,7 +485,7 @@ describe('session', () => { assert.deepStrictEqual(respose.messages, [ { role: 'system', - content: [{ text: ' foo=bar' }], + content: [{ text: 'foo=bar' }], metadata: { preamble: true }, }, { @@ -502,7 +494,7 @@ describe('session', () => { }, { content: [ - { text: 'Echo: system: foo=bar,hi' }, + { text: 'Echo: system: foo=bar,hi' }, { text: '; config: {"temperature":1}' }, ], role: 'model', diff --git a/js/package.json b/js/package.json index 33222d047..7e9676ba5 100644 --- a/js/package.json +++ b/js/package.json @@ -4,11 +4,10 @@ "preinstall": "npx only-allow pnpm", "build": "pnpm install && pnpm build:libs && pnpm build:docsnippets && pnpm build:testapps", "build:libs": "pnpm build:core && pnpm build:genkit && pnpm build:noncore ", - "build:core": "pnpm -r --workspace-concurrency 1 -F core -F ai -F flow build && pnpm build:dotprompt", + "build:core": "pnpm -r --workspace-concurrency 1 -F core -F ai -F flow build", "build:genkit": "pnpm -F genkit build", - "build:noncore": "pnpm -r --workspace-concurrency 0 -F \"./plugins/**\" -F \"!./plugins/dotprompt\" build", + "build:noncore": "pnpm -r --workspace-concurrency 0 -F \"./plugins/**\" build", "build:testapps": "pnpm -r --workspace-concurrency 0 -F \"./testapps/**\" build", - "build:dotprompt": "cd plugins/dotprompt && pnpm build", "build:docsnippets": "cd doc-snippets && pnpm install && pnpm build", "pack:all": "(mkdir ../dist || true) && npm-run-all pack:core pack:ai pack:genkit pack:plugins", "pack:core": "cd core && pnpm pack --pack-destination ../../dist", diff --git a/js/plugins/dotprompt/.npmignore b/js/plugins/dotprompt/.npmignore deleted file mode 100644 index 40b878db5..000000000 --- a/js/plugins/dotprompt/.npmignore +++ /dev/null @@ -1 +0,0 @@ -node_modules/ \ No newline at end of file diff --git a/js/plugins/dotprompt/LICENSE b/js/plugins/dotprompt/LICENSE deleted file mode 100644 index 26a870243..000000000 --- a/js/plugins/dotprompt/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - \ No newline at end of file diff --git a/js/plugins/dotprompt/README.md b/js/plugins/dotprompt/README.md deleted file mode 100644 index 324aee45e..000000000 --- a/js/plugins/dotprompt/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Genkit - -The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. - -Usage information and reference details can be found in [Genkit documentation](https://firebase.google.com/docs/genkit). - -License: Apache 2.0 diff --git a/js/plugins/dotprompt/package.json b/js/plugins/dotprompt/package.json deleted file mode 100644 index b4f1621bf..000000000 --- a/js/plugins/dotprompt/package.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "name": "@genkit-ai/dotprompt", - "description": "Genkit AI framework `.prompt` file format and management library.", - "keywords": [ - "genkit", - "ai", - "genai", - "generative-ai", - "prompting", - "templating" - ], - "version": "1.0.0-rc.5", - "type": "commonjs", - "scripts": { - "check": "tsc", - "compile": "tsup-node", - "build:clean": "rimraf ./lib", - "build": "npm-run-all build:clean check compile", - "build:watch": "tsup-node --watch", - "test": "tsx --test tests/*_test.ts" - }, - "repository": { - "type": "git", - "url": "https://github.com/firebase/genkit.git", - "directory": "js/plugins/dotprompt" - }, - "author": "genkit", - "license": "Apache-2.0", - "dependencies": { - "front-matter": "^4.0.2", - "handlebars": "^4.7.8", - "node-fetch": "^3.3.2", - "@genkit-ai/ai": "workspace:*", - "@genkit-ai/core": "workspace:*" - }, - "devDependencies": { - "@types/node": "^20.11.16", - "npm-run-all": "^4.1.5", - "rimraf": "^6.0.1", - "tsup": "^8.3.5", - "tsx": "^4.19.2", - "typescript": "^4.9.0", - "yaml": "^2.4.1" - }, - "types": "./lib/index.d.ts", - "exports": { - ".": { - "require": "./lib/index.js", - "import": "./lib/index.mjs", - "types": "./lib/index.d.ts", - "default": "./lib/index.js" - } - } -} diff --git a/js/plugins/dotprompt/src/index.ts b/js/plugins/dotprompt/src/index.ts deleted file mode 100644 index 439f7e28c..000000000 --- a/js/plugins/dotprompt/src/index.ts +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright 2024 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 { Registry } from '@genkit-ai/core/registry'; -import { readFileSync } from 'fs'; -import { basename } from 'path'; -import { toFrontmatter } from './metadata.js'; -import { - Dotprompt, - DotpromptRef, - defineDotprompt, - type DotpromptAction, - type PromptGenerateOptions, -} from './prompt.js'; -import { loadPromptFolder, lookupPrompt } from './registry.js'; - -export { type PromptMetadata } from './metadata.js'; -export { defineHelper, definePartial } from './template.js'; -export { - Dotprompt, - defineDotprompt, - loadPromptFolder, - toFrontmatter, - type DotpromptAction, - type PromptGenerateOptions, -}; - -export interface DotpromptPluginOptions { - // Directory to look for .prompt files. - // - // Note: This directory will be searched recursively, and any sub-directory - // paths will be included in the prompt name. E.g. - if a prompt file is - // located at `
/foo/bar.prompt`, the prompt name will be `foo-bar`. - dir: string; -} - -export async function prompt( - registry: Registry, - name: string, - options?: { variant?: string; dir?: string } -): Promise> { - return (await lookupPrompt( - registry, - name, - options?.variant, - options?.dir ?? './prompts' - )) as Dotprompt; -} - -export function promptRef( - name: string, - options?: { variant?: string; dir?: string } -): DotpromptRef { - return new DotpromptRef(name, options); -} - -export function loadPromptFile(registry: Registry, path: string): Dotprompt { - return Dotprompt.parse( - registry, - basename(path).split('.')[0], - readFileSync(path, 'utf-8') - ); -} - -export async function loadPromptUrl( - registry: Registry, - name: string, - url: string -): Promise { - const fetch = (await import('node-fetch')).default; - const response = await fetch(url); - const text = await response.text(); - return Dotprompt.parse(registry, name, text); -} diff --git a/js/plugins/dotprompt/src/metadata.ts b/js/plugins/dotprompt/src/metadata.ts deleted file mode 100644 index 288e89bee..000000000 --- a/js/plugins/dotprompt/src/metadata.ts +++ /dev/null @@ -1,205 +0,0 @@ -/** - * Copyright 2024 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. - */ - -// -// IMPORTANT: Please keep type definitions in sync with -// genkit-tools/src/types/prompt.ts -// - -import { - GenerationCommonConfigSchema, - ModelArgument, - ModelMiddleware, -} from '@genkit-ai/ai/model'; -import { ToolArgument } from '@genkit-ai/ai/tool'; -import { z } from '@genkit-ai/core'; -import { Registry } from '@genkit-ai/core/registry'; -import { JSONSchema, parseSchema, toJsonSchema } from '@genkit-ai/core/schema'; -import { picoschema } from './picoschema.js'; - -/** - * Metadata for a prompt. - */ -export interface PromptMetadata< - Input extends z.ZodTypeAny = z.ZodTypeAny, - Options extends z.ZodTypeAny = z.ZodTypeAny, -> { - /** The name of the prompt. */ - name?: string; - - /** Description (intent) of the prompt, used when prompt passed as tool to an LLM. */ - description?: string; - - /** The variant name for the prompt. */ - variant?: string; - - /** The name of the model to use for this prompt, e.g. `vertexai/gemini-1.0-pro` */ - model?: ModelArgument; - - /** Names of tools (registered separately) to allow use of in this prompt. */ - tools?: ToolArgument[]; - - /** Specifies how tools should be called by the model. */ - toolChoice?: 'auto' | 'required' | 'none'; - - /** Model configuration. Not all models support all options. */ - config?: z.infer; - - input?: { - /** Defines the default input variable values to use if none are provided. */ - default?: any; - /** Zod schema defining the input variables. */ - schema?: Input; - /** - * Defines the input variables that can be passed into the template in JSON schema form. - * If not supplied, any object will be accepted. `{type: "object"}` is defaulted if not - * supplied. - */ - jsonSchema?: JSONSchema; - }; - - /** Defines the expected model output format. */ - output?: { - /** Desired output format for this prompt. */ - format?: 'json' | 'text' | 'media'; - /** Zod schema defining the output structure (cannot be specified with non-json format). */ - schema?: z.ZodTypeAny; - /** JSON schema of desired output (cannot be specified with non-json format). */ - jsonSchema?: JSONSchema; - }; - - /** Arbitrary metadata to be used by code, tools, and libraries. */ - metadata?: Record; - - /** Middleware to be used with this model call. */ - use?: ModelMiddleware[]; -} - -/** - * Formal schema for prompt YAML frontmatter. - */ -export const PromptFrontmatterSchema = z.object({ - name: z.string().optional(), - description: z.string().optional(), - variant: z.string().optional(), - model: z.string().optional(), - tools: z.array(z.string()).optional(), - config: GenerationCommonConfigSchema.passthrough().optional(), - input: z - .object({ - default: z.any(), - schema: z.unknown(), - }) - .optional(), - output: z - .object({ - format: z.enum(['json', 'text', 'media']).optional(), - schema: z.unknown().optional(), - }) - .optional(), - metadata: z.record(z.unknown()).optional(), -}); - -export type PromptFrontmatter = z.infer; - -function stripUndefinedOrNull(obj: any) { - if (typeof obj !== 'object' || obj === null) { - return obj; - } - - for (const key in obj) { - if (obj[key] === undefined || obj[key] === null) { - delete obj[key]; - } else if (typeof obj[key] === 'object') { - stripUndefinedOrNull(obj[key]); // Recurse into nested objects - } - } - return obj; -} - -function fmSchemaToSchema(registry: Registry, fmSchema: any) { - if (!fmSchema) return {}; - if (typeof fmSchema === 'string') return registry.lookupSchema(fmSchema); - return { jsonSchema: picoschema(fmSchema) }; -} - -export function toMetadata( - registry: Registry, - attributes: unknown -): Partial { - const fm = parseSchema>(attributes, { - schema: PromptFrontmatterSchema, - }); - - let input: PromptMetadata['input'] | undefined; - if (fm.input) { - input = { - default: fm.input.default, - ...fmSchemaToSchema(registry, fm.input.schema), - }; - } - - let output: PromptMetadata['output'] | undefined; - if (fm.output) { - output = { - format: fm.output.format, - ...fmSchemaToSchema(registry, fm.output.schema), - }; - } - - return stripUndefinedOrNull({ - name: fm.name, - description: fm.description, - variant: fm.variant, - model: fm.model, - config: fm.config, - input, - output, - metadata: fm.metadata, - tools: fm.tools, - }); -} - -export function toFrontmatter(md: PromptMetadata): PromptFrontmatter { - return stripUndefinedOrNull({ - name: md.name, - variant: md.variant, - model: typeof md.model === 'string' ? md.model : md.model?.name, - config: md.config, - input: md.input - ? { - default: md.input.default, - schema: toJsonSchema({ - schema: md.input.schema, - jsonSchema: md.input.jsonSchema, - }), - } - : undefined, - output: md.output - ? { - format: md.output.format, - schema: toJsonSchema({ - schema: md.output.schema, - jsonSchema: md.output.jsonSchema, - }), - } - : undefined, - metadata: md.metadata, - tools: md.tools?.map((t) => - typeof t === 'string' ? t : (t as any).__action?.name || (t as any).name - ), - }); -} diff --git a/js/plugins/dotprompt/src/picoschema.ts b/js/plugins/dotprompt/src/picoschema.ts deleted file mode 100644 index 63541d491..000000000 --- a/js/plugins/dotprompt/src/picoschema.ts +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright 2024 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. - */ - -const JSON_SCHEMA_SCALAR_TYPES = [ - 'string', - 'boolean', - 'null', - 'number', - 'integer', - 'any', -]; - -const WILDCARD_PROPERTY_NAME = '(*)'; - -import { JSONSchema } from '@genkit-ai/core/schema'; - -export function picoschema(schema: unknown): JSONSchema | null { - if (!schema) return null; - // if there's a JSON schema-ish type at the top level, treat as JSON schema - if ( - [...JSON_SCHEMA_SCALAR_TYPES, 'object', 'array'].includes( - (schema as any)?.type - ) - ) { - return schema; - } - - if (typeof (schema as any)?.properties === 'object') { - return { ...schema, type: 'object' }; - } - - return parsePico(schema); -} - -function extractDescription(input: string): [string, string | null] { - if (!input.includes(',')) return [input, null]; - - const match = input.match(/(.*?), *(.*)$/); - return [match![1], match![2]]; -} - -function parsePico(obj: any, path: string[] = []): JSONSchema { - if (typeof obj === 'string') { - const [type, description] = extractDescription(obj); - if (!JSON_SCHEMA_SCALAR_TYPES.includes(type)) { - throw new Error(`Picoschema: Unsupported scalar type '${type}'.`); - } - - if (type === 'any') { - return description ? { description } : {}; - } - - return description ? { type, description } : { type }; - } else if (typeof obj !== 'object') { - throw new Error( - 'Picoschema: only consists of objects and strings. Got: ' + - JSON.stringify(obj) - ); - } - - const schema: JSONSchema = { - type: 'object', - properties: {}, - required: [], - additionalProperties: false, - }; - - for (const key in obj) { - // wildcard property - if (key === WILDCARD_PROPERTY_NAME) { - schema.additionalProperties = parsePico(obj[key], [...path, key]); - continue; - } - - const [name, typeInfo] = key.split('('); - const isOptional = name.endsWith('?'); - const propertyName = isOptional ? name.slice(0, -1) : name; - - if (!isOptional) { - schema.required.push(propertyName); - } - - if (!typeInfo) { - const prop = parsePico(obj[key], [...path, key]); - // make all optional fields also nullable - if (isOptional && typeof prop.type === 'string') { - prop.type = [prop.type, 'null']; - } - schema.properties[propertyName] = prop; - continue; - } - - const [type, description] = extractDescription( - typeInfo.substring(0, typeInfo.length - 1) - ); - if (type === 'array') { - schema.properties[propertyName] = { - type: isOptional ? ['array', 'null'] : 'array', - items: parsePico(obj[key], [...path, key]), - }; - } else if (type === 'object') { - const prop = parsePico(obj[key], [...path, key]); - if (isOptional) prop.type = [prop.type, 'null']; - schema.properties[propertyName] = prop; - } else if (type === 'enum') { - const prop = { enum: obj[key] }; - if (isOptional) prop.enum.push(null); - schema.properties[propertyName] = prop; - } else { - throw new Error( - "Picoschema: parenthetical types must be 'object' or 'array', got: " + - type - ); - } - if (description) { - schema.properties[propertyName].description = description; - } - } - - if (!schema.required.length) delete schema.required; - return schema; -} diff --git a/js/plugins/dotprompt/src/prompt.ts b/js/plugins/dotprompt/src/prompt.ts deleted file mode 100644 index fe465b8de..000000000 --- a/js/plugins/dotprompt/src/prompt.ts +++ /dev/null @@ -1,436 +0,0 @@ -/** - * Copyright 2024 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 { - definePrompt, - generate, - GenerateOptions, - GenerateResponse, - generateStream, - GenerateStreamResponse, - PromptAction, - toGenerateRequest, -} from '@genkit-ai/ai'; -import { MessageData, ModelArgument } from '@genkit-ai/ai/model'; -import { DocumentData } from '@genkit-ai/ai/retriever'; -import { getCurrentSession } from '@genkit-ai/ai/session'; -import { GenkitError, z } from '@genkit-ai/core'; -import { Registry } from '@genkit-ai/core/registry'; -import { parseSchema } from '@genkit-ai/core/schema'; -import { - runInNewSpan, - setCustomMetadataAttribute, - SPAN_TYPE_ATTR, -} from '@genkit-ai/core/tracing'; -import { createHash } from 'crypto'; -import fm, { FrontMatterResult } from 'front-matter'; -import { - PromptFrontmatter, - PromptMetadata, - toFrontmatter, - toMetadata, -} from './metadata.js'; -import { lookupPrompt, registryDefinitionKey } from './registry.js'; -import { compile } from './template.js'; - -export type PromptData = PromptFrontmatter & { template: string }; - -export type DotpromptAction = PromptAction & { - __dotprompt: Dotprompt; -}; - -export type PromptGenerateOptions< - V = unknown, - CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, -> = Omit< - GenerateOptions, - 'prompt' | 'input' | 'model' -> & { - model?: ModelArgument; - input?: V; -}; - -interface RenderMetadata { - docs?: DocumentData[]; - messages?: MessageData[]; -} - -export class Dotprompt implements PromptMetadata { - name: string; - description?: string; - variant?: string; - hash: string; - - template: string; - - model?: PromptMetadata['model']; - metadata: PromptMetadata['metadata']; - input?: PromptMetadata['input']; - output?: PromptMetadata['output']; - tools?: PromptMetadata['tools']; - toolChoice?: PromptMetadata['toolChoice']; - config?: PromptMetadata['config']; - use?: PromptMetadata['use']; - - private _promptAction?: PromptAction; - - private _render: ( - input: I, - options?: RenderMetadata, - data?: Record - ) => MessageData[]; - - static parse(registry: Registry, name: string, source: string) { - try { - const fmResult = (fm as any)(source.trimStart(), { - allowUnsafe: false, - }) as FrontMatterResult; - - return new Dotprompt( - registry, - { - ...toMetadata(registry, fmResult.attributes), - name, - } as PromptMetadata, - fmResult.body - ); - } catch (e: any) { - throw new GenkitError({ - source: 'Dotprompt', - status: 'INVALID_ARGUMENT', - message: `Error parsing YAML frontmatter of '${name}' prompt: ${e.stack}`, - }); - } - } - - static fromAction(registry: Registry, action: PromptAction): Dotprompt { - const { template, ...options } = action.__action.metadata!.prompt; - const pm = options as PromptMetadata; - if (pm.input?.schema) { - pm.input.jsonSchema = options.input?.schema; - delete pm.input.schema; - } - if (pm.output?.schema) { - pm.output.jsonSchema = options.output?.schema; - } - const prompt = new Dotprompt( - registry, - options as PromptMetadata, - template, - action - ); - return prompt; - } - - constructor( - private registry: Registry, - options: PromptMetadata, - template: string, - action?: PromptAction - ) { - this.name = options.name || 'untitledPrompt'; - this.description = options.description; - this.variant = options.variant; - this.model = options.model; - this.input = options.input || { schema: z.any() }; - this.output = options.output; - this.tools = options.tools; - this.toolChoice = options.toolChoice; - this.config = options.config; - this.use = options.use; - this.template = template; - this.hash = createHash('sha256').update(JSON.stringify(this)).digest('hex'); - - this._render = compile(this.template, options); - this._promptAction = action; - } - - /** - * Renders all of the prompt's text parts into a raw string. - * - * @param input User input to the prompt template. - * @param options Optional context and/or history for the prompt template. - * @returns all of the text parts concatenated into a string. - */ - - renderText(input: I, options?: RenderMetadata): string { - const result = this.renderMessages(input, options); - if (result.length !== 1) { - throw new Error("Multi-message prompt can't be rendered as text."); - } - let out = ''; - for (const part of result[0].content) { - if (!part.text) { - throw new Error("Multimodal prompt can't be rendered as text."); - } - out += part.text; - } - return out; - } - - /** - * Renders the prompt template into an array of messages. - * - * @param input User input to the prompt template - * @param options optional context and/or history for the prompt template. - * @returns an array of messages representing an exchange between a user and a model. - */ - renderMessages(input?: I, options?: RenderMetadata): MessageData[] { - let sessionStateData: Record | undefined = undefined; - if (getCurrentSession(this.registry)) { - sessionStateData = { state: getCurrentSession(this.registry)?.state }; - } - input = parseSchema(input, { - schema: this.input?.schema, - jsonSchema: this.input?.jsonSchema, - }); - return this._render( - { ...this.input?.default, ...input }, - options, - sessionStateData - ); - } - - toJSON(): PromptData { - return { ...toFrontmatter(this), template: this.template }; - } - - define(options?: { ns?: string; description?: string }): void { - this._promptAction = definePrompt( - this.registry, - { - name: registryDefinitionKey(this.name, this.variant, options?.ns), - description: options?.description ?? this.description, - inputSchema: this.input?.schema, - inputJsonSchema: this.input?.jsonSchema, - metadata: { - type: 'prompt', - prompt: this.toJSON(), - }, - }, - async (input?: I) => - toGenerateRequest(this.registry, this.render({ input })) - ); - (this._promptAction as DotpromptAction).__dotprompt = this; - } - - get promptAction(): PromptAction | undefined { - return this._promptAction; - } - - private _generateOptions< - O extends z.ZodTypeAny = z.ZodTypeAny, - CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, - >(options: PromptGenerateOptions): GenerateOptions { - const messages = this.renderMessages(options.input, { - messages: options.messages, - docs: options.docs, - }); - let renderedPrompt; - let renderedMessages; - if (messages.length > 0 && messages[messages.length - 1].role === 'user') { - renderedPrompt = messages[messages.length - 1].content; - renderedMessages = messages.slice(0, messages.length - 1); - } else { - renderedPrompt = undefined; - renderedMessages = messages; - } - const res = { - model: options.model || this.model!, - config: { ...this.config, ...options.config }, - messages: renderedMessages, - prompt: renderedPrompt, - docs: options.docs, - output: { - format: options.output?.format || this.output?.format || undefined, - schema: options.output?.schema || this.output?.schema, - jsonSchema: options.output?.jsonSchema || this.output?.jsonSchema, - }, - tools: (options.tools || []).concat(this.tools || []), - onChunk: options.onChunk ?? options.streamingCallback, - returnToolRequests: options.returnToolRequests, - maxTurns: options.maxTurns, - } as GenerateOptions; - const middleware = (options.use || []).concat(this.use || []); - if (middleware.length > 0) { - res.use = middleware; - } - if (options.toolChoice || this.toolChoice) { - res.toolChoice = options.toolChoice ?? this.toolChoice; - } - return res; - } - - /** - * Renders the prompt template based on user input. - * - * @param opt Options for the prompt template, including user input variables and custom model configuration options. - * @returns a `GenerateOptions` object to be used with the `generate()` function from @genkit-ai/ai. - */ - render< - CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - >( - opt: PromptGenerateOptions - ): GenerateOptions { - return this._generateOptions(opt); - } - - async renderInNewSpan< - CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - >(opt: PromptGenerateOptions): Promise> { - const spanName = this.variant ? `${this.name}.${this.variant}` : this.name; - return runInNewSpan( - this.registry, - { - metadata: { - name: spanName, - input: opt, - }, - labels: { - [SPAN_TYPE_ATTR]: 'dotprompt', - }, - }, - async (metadata) => { - setCustomMetadataAttribute( - this.registry, - 'prompt_fingerprint', - this.hash - ); - const generateOptions = this._generateOptions(opt); - metadata.output = generateOptions; - return generateOptions; - } - ); - } - - /** - * Generates a response by rendering the prompt template with given user input and then calling the model. - * - * @param opt Options for the prompt template, including user input variables and custom model configuration options. - * @returns the model response as a promise of `GenerateResponse`. - */ - async generate< - CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - >( - opt: PromptGenerateOptions - ): Promise>> { - const renderedOpts = this.renderInNewSpan(opt); - return generate(this.registry, renderedOpts); - } - - /** - * Generates a streaming response by rendering the prompt template with given user input and then calling the model. - * - * @param opt Options for the prompt template, including user input variables and custom model configuration options. - * @returns the model response as a promise of `GenerateStreamResponse`. - */ - async generateStream( - opt: PromptGenerateOptions - ): Promise { - const renderedOpts = await this.renderInNewSpan(opt); - return generateStream(this.registry, renderedOpts); - } -} - -export class DotpromptRef { - name: string; - variant?: string; - dir?: string; - private _prompt?: Dotprompt; - - constructor( - name: string, - options?: { - variant?: string; - dir?: string; - } - ) { - this.name = name; - this.variant = options?.variant; - this.dir = options?.dir; - } - - /** Loads the prompt which is referenced. */ - async loadPrompt(registry: Registry): Promise> { - if (this._prompt) return this._prompt; - this._prompt = (await lookupPrompt( - registry, - this.name, - this.variant, - this.dir - )) as Dotprompt; - return this._prompt; - } - - /** - * Generates a response by rendering the prompt template with given user input and then calling the model. - * - * @param opt Options for the prompt template, including user input variables and custom model configuration options. - * @returns the model response as a promise of `GenerateResponse`. - */ - - async generate< - CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - >( - registry: Registry, - opt: PromptGenerateOptions - ): Promise>> { - const prompt = await this.loadPrompt(registry); - return prompt.generate(opt); - } - - /** - * Renders the prompt template based on user input. - * - * @param opt Options for the prompt template, including user input variables and custom model configuration options. - * @returns a `GenerateOptions` object to be used with the `generate()` function from @genkit-ai/ai. - */ - async render< - CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, - O extends z.ZodTypeAny = z.ZodTypeAny, - >( - registry: Registry, - - opt: PromptGenerateOptions - ): Promise> { - const prompt = await this.loadPrompt(registry); - return prompt.render(opt); - } -} - -/** - * Define a dotprompt in code. This function is offered as an alternative to definitions in .prompt files. - * - * @param options the prompt definition, including its name, variant and model. Any options from .prompt file front matter are accepted. - * @param template a string template, comparable to the main body of a prompt file. - * @returns the newly defined prompt. - */ -export function defineDotprompt< - I extends z.ZodTypeAny = z.ZodTypeAny, - CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, ->( - registry: Registry, - options: PromptMetadata, - template: string -): Dotprompt> { - const prompt = new Dotprompt(registry, options, template); - prompt.define({ description: options.description }); - return prompt; -} diff --git a/js/plugins/dotprompt/src/registry.ts b/js/plugins/dotprompt/src/registry.ts deleted file mode 100644 index 42a35acf8..000000000 --- a/js/plugins/dotprompt/src/registry.ts +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright 2024 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 { PromptAction } from '@genkit-ai/ai'; -import { GenkitError } from '@genkit-ai/core'; -import { logger } from '@genkit-ai/core/logging'; -import { Registry } from '@genkit-ai/core/registry'; -import { existsSync, readdir, readFileSync } from 'fs'; -import { basename, join, resolve } from 'path'; -import { Dotprompt } from './prompt.js'; -import { definePartial } from './template.js'; - -export function registryDefinitionKey( - name: string, - variant?: string, - ns?: string -) { - // "ns/prompt.variant" where ns and variant are optional - return `${ns ? `${ns}/` : ''}${name}${variant ? `.${variant}` : ''}`; -} - -export function registryLookupKey(name: string, variant?: string, ns?: string) { - return `/prompt/${registryDefinitionKey(name, variant, ns)}`; -} - -export async function lookupPrompt( - registry: Registry, - name: string, - variant?: string, - dir: string = './prompts' -): Promise { - let registryPrompt = - (await registry.lookupAction(registryLookupKey(name, variant))) || - (await registry.lookupAction( - registryLookupKey(name, variant, 'dotprompt') - )); - if (registryPrompt) { - return Dotprompt.fromAction(registry, registryPrompt as PromptAction); - } else { - // Handle the case where initialization isn't complete - // or a file was added after the prompt folder was loaded. - return maybeLoadPrompt(registry, dir, name, variant); - } -} - -async function maybeLoadPrompt( - registry: Registry, - dir: string, - name: string, - variant?: string -): Promise { - const expectedFileName = `${name}${variant ? `.${variant}` : ''}.prompt`; - const promptFolder = resolve(dir); - const promptExists = existsSync(join(promptFolder, expectedFileName)); - if (promptExists) { - return loadPrompt(registry, promptFolder, expectedFileName); - } else { - throw new GenkitError({ - source: 'dotprompt', - status: 'NOT_FOUND', - message: `Could not find '${expectedFileName}' in the prompts folder.`, - }); - } -} - -export async function loadPromptFolder( - registry: Registry, - dir: string = './prompts', - ns: string -): Promise { - const promptsPath = resolve(dir); - return new Promise((resolve, reject) => { - if (existsSync(promptsPath)) { - readdir( - promptsPath, - { - withFileTypes: true, - recursive: true, - }, - (err, dirEnts) => { - if (err) { - reject(err); - } else { - dirEnts.forEach(async (dirEnt) => { - if (dirEnt.isFile() && dirEnt.name.endsWith('.prompt')) { - if (dirEnt.name.startsWith('_')) { - const partialName = dirEnt.name.substring( - 1, - dirEnt.name.length - 7 - ); - definePartial( - partialName, - readFileSync(join(dirEnt.path, dirEnt.name), { - encoding: 'utf8', - }) - ); - logger.debug( - `Registered Dotprompt partial "${partialName}" from "${join(dirEnt.path, dirEnt.name)}"` - ); - } else { - // If this prompt is in a subdirectory, we need to include that - // in the namespace to prevent naming conflicts. - let prefix = ''; - if (promptsPath !== dirEnt.path) { - prefix = dirEnt.path - .replace(`${promptsPath}/`, '') - .replace(/\//g, '-'); - } - loadPrompt(registry, dirEnt.path, dirEnt.name, prefix, ns); - } - } - }); - resolve(); - } - } - ); - } else { - resolve(); - } - }); -} - -export function loadPrompt( - registry: Registry, - path: string, - filename: string, - prefix = '', - ns = 'dotprompt' -): Dotprompt { - let name = `${prefix ? `${prefix}-` : ''}${basename(filename, '.prompt')}`; - let variant: string | null = null; - if (name.includes('.')) { - const parts = name.split('.'); - name = parts[0]; - variant = parts[1]; - } - const source = readFileSync(join(path, filename), 'utf8'); - const prompt = Dotprompt.parse(registry, name, source); - if (variant) { - prompt.variant = variant; - } - prompt.define({ ns }); - return prompt; -} diff --git a/js/plugins/dotprompt/src/template.ts b/js/plugins/dotprompt/src/template.ts deleted file mode 100644 index 3f0b89290..000000000 --- a/js/plugins/dotprompt/src/template.ts +++ /dev/null @@ -1,188 +0,0 @@ -/** - * Copyright 2024 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 { MediaPart, MessageData, Part, Role } from '@genkit-ai/ai/model'; -import { DocumentData } from '@genkit-ai/ai/retriever'; -import * as Handlebars from 'handlebars'; -import { PromptMetadata } from './metadata.js'; - -const Promptbars: typeof Handlebars = - global['dotprompt.handlebars'] || Handlebars.create(); -global['dotprompt.handlebars'] = Promptbars; - -function jsonHelper(serializable: any, options: { hash: { indent?: number } }) { - return new Promptbars.SafeString( - JSON.stringify(serializable, null, options.hash.indent || 0) - ); -} -Promptbars.registerHelper('json', jsonHelper); - -function roleHelper(role: string) { - return new Promptbars.SafeString(`<<>>`); -} -Promptbars.registerHelper('role', roleHelper); - -function historyHelper() { - return new Promptbars.SafeString('<<>>'); -} -Promptbars.registerHelper('history', historyHelper); - -function sectionHelper(name: string) { - return new Promptbars.SafeString(`<<>>`); -} -Promptbars.registerHelper('section', sectionHelper); - -function mediaHelper(options: Handlebars.HelperOptions) { - return new Promptbars.SafeString( - `<<>>` - ); -} -Promptbars.registerHelper('media', mediaHelper); - -const ROLE_REGEX = /(<<>>/g; - -function toMessages( - renderedString: string, - options?: { context?: DocumentData[]; messages?: MessageData[] } -): MessageData[] { - let currentMessage: { role: string; source: string } = { - role: 'user', - source: '', - }; - const messageSources: { - role: string; - source?: string; - content?: MessageData['content']; - metadata?: Record; - }[] = [currentMessage]; - - for (const piece of renderedString - .split(ROLE_REGEX) - .filter((s) => s.trim() !== '')) { - if (piece.startsWith('<< { - return { - ...m, - metadata: { ...(m.metadata || {}), purpose: 'history' }, - }; - }) || []) - ); - currentMessage = { role: 'model', source: '' }; - messageSources.push(currentMessage); - } else { - currentMessage.source += piece; - } - } - - const messages: MessageData[] = messageSources - .filter((ms) => ms.content || ms.source) - .map((m) => { - const out: MessageData = { - role: m.role as Role, - content: m.content || toParts(m.source!), - }; - if (m.metadata) out.metadata = m.metadata; - return out; - }); - - if ( - !options?.messages || - messages.find((m) => m.metadata?.purpose === 'history') - ) - return messages; - - if (messages.at(-1)?.role === 'user') { - return [ - ...messages.slice(0, -1), - ...options.messages, - messages.at(-1), - ] as MessageData[]; - } - - return [...messages, ...options.messages] as MessageData[]; -} - -const PART_REGEX = /(<<>>/g; - -function toParts(source: string): Part[] { - const parts: Part[] = []; - const pieces = source.split(PART_REGEX).filter((s) => s.trim() !== ''); - for (let i = 0; i < pieces.length; i++) { - const piece = pieces[i]; - if (piece.startsWith('<<( - source: string, - metadata: PromptMetadata -) { - const renderString = Promptbars.compile(source, { - knownHelpers: { - json: true, - section: true, - media: true, - role: true, - history: true, - }, - }); - - return ( - input: Variables, - options?: { context?: DocumentData[]; messages?: MessageData[] }, - data?: Record - ) => { - const renderedString = renderString(input, { - data: { - metadata: { prompt: metadata, context: options?.context || null }, - ...data, - }, - }); - return toMessages(renderedString, options); - }; -} - -export function defineHelper(name: string, fn: Handlebars.HelperDelegate) { - Promptbars.registerHelper(name, fn); -} - -export function definePartial(name: string, source: string) { - Promptbars.registerPartial(name, source); -} diff --git a/js/plugins/dotprompt/tests/picoschema_test.ts b/js/plugins/dotprompt/tests/picoschema_test.ts deleted file mode 100644 index d341e3b8f..000000000 --- a/js/plugins/dotprompt/tests/picoschema_test.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 2024 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 * as assert from 'assert'; -import { readFileSync } from 'fs'; -import { describe, it } from 'node:test'; -import { parse } from 'yaml'; -import { picoschema } from '../src/picoschema'; - -describe('picoschema()', () => { - const tests = parse(readFileSync('tests/picoschema_tests.yaml', 'utf8')); - for (const test of tests) { - it(test.description, () => { - const got = picoschema(parse(test.yaml).schema); - assert.deepEqual(got, test.want); - }); - } -}); diff --git a/js/plugins/dotprompt/tests/picoschema_tests.yaml b/js/plugins/dotprompt/tests/picoschema_tests.yaml deleted file mode 100644 index 2e0834228..000000000 --- a/js/plugins/dotprompt/tests/picoschema_tests.yaml +++ /dev/null @@ -1,173 +0,0 @@ -- description: simple scalar, no description - yaml: 'schema: string' - want: { type: string } - -- description: simple scalar, with description - yaml: 'schema: number, the description' - want: { type: number, description: 'the description' } - -- description: simple scalar, with description (no whitespace) - yaml: 'schema: number,the description' - want: { type: number, description: 'the description' } - -- description: simple scalar, with description (comma in description) - yaml: 'schema: number,the description, which has, multiple commas' - want: - { type: number, description: 'the description, which has, multiple commas' } - -- description: simple scalar, with description (extra whitespace) - yaml: 'schema: number, the description' - want: { type: number, description: 'the description' } - -- description: simple object - yaml: | - schema: - field1: boolean - field2: string - want: - { - type: object, - additionalProperties: false, - properties: { field1: { type: boolean }, field2: { type: string } }, - required: ['field1', 'field2'], - } - -- description: required field - yaml: | - schema: - req: string, required field - nonreq?: boolean, optional field - want: - { - type: object, - additionalProperties: false, - properties: - { - req: { type: string, description: 'required field' }, - nonreq: { type: [boolean, 'null'], description: 'optional field' }, - }, - required: ['req'], - } - -- description: array of scalars, with and without description - yaml: | - schema: - tags(array, list of tags): string, the tag - vector(array): number - want: - { - type: object, - additionalProperties: false, - properties: - { - tags: - { - type: array, - description: 'list of tags', - items: { type: string, description: 'the tag' }, - }, - vector: { type: array, items: { type: number } }, - }, - required: ['tags', 'vector'], - } - -- description: nested object in array and out - yaml: | - schema: - obj?(object, a nested object): - nest1?: string - arr(array, array of objects): - nest2?: boolean - want: - { - type: object, - additionalProperties: false, - properties: - { - obj: - { - type: [object, 'null'], - description: 'a nested object', - additionalProperties: false, - properties: { nest1: { type: [string, 'null'] } }, - }, - arr: - { - type: array, - description: 'array of objects', - items: - { - type: object, - additionalProperties: false, - properties: { nest2: { type: [boolean, 'null'] } }, - }, - }, - }, - required: ['arr'], - } - -- description: simple json schema type - yaml: | - schema: - type: string - want: { type: string } - -- description: inferred json schema from properties - yaml: | - schema: - properties: - foo: {type: string} - want: { type: object, properties: { foo: { type: string } } } - -- description: enum field - yaml: | - schema: - color?(enum, the enum): [RED, BLUE, GREEN] - want: - { - type: object, - properties: - { - color: - { description: 'the enum', enum: ['RED', 'BLUE', 'GREEN', null] }, - }, - additionalProperties: false, - } - -- description: any field - yaml: | - schema: - first: any - second?: any, could be anything - want: - { - type: object, - properties: { first: {}, second: { description: 'could be anything' } }, - additionalProperties: false, - required: ['first'], - } - -- description: wildcard fields with other fields - yaml: | - schema: - otherField: string, another string - (*): any, whatever you want - want: - { - additionalProperties: { description: 'whatever you want' }, - properties: - { otherField: { description: 'another string', type: string } }, - required: ['otherField'], - type: object, - } - -- description: wildcard fields without other fields - yaml: | - schema: - (*): number, lucky number - want: - { - additionalProperties: { type: number, description: 'lucky number' }, - properties: {}, - type: object, - } diff --git a/js/plugins/dotprompt/tests/prompt_test.ts b/js/plugins/dotprompt/tests/prompt_test.ts deleted file mode 100644 index 9cccb0d0a..000000000 --- a/js/plugins/dotprompt/tests/prompt_test.ts +++ /dev/null @@ -1,527 +0,0 @@ -/** - * Copyright 2024 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 { defineModel, ModelAction } from '@genkit-ai/ai/model'; -import { z } from '@genkit-ai/core'; -import { Registry } from '@genkit-ai/core/registry'; -import { - defineJsonSchema, - defineSchema, - toJsonSchema, - ValidationError, -} from '@genkit-ai/core/schema'; -import * as assert from 'assert'; -import { beforeEach, describe, it } from 'node:test'; -import { defineDotprompt, Dotprompt, prompt, promptRef } from '../src/index.js'; -import { PromptMetadata } from '../src/metadata.js'; - -function testPrompt( - registry: Registry, - model: ModelAction, - template: string, - options?: Partial -): Dotprompt { - return new Dotprompt(registry, { name: 'test', model, ...options }, template); -} - -describe('Prompt', () => { - let registry: Registry; - beforeEach(() => { - registry = new Registry(); - }); - - describe('#render', () => { - it('should render variables', () => { - const model = defineModel( - registry, - { name: 'echo', supports: { tools: true } }, - async (input) => ({ - message: input.messages[0], - finishReason: 'stop', - }) - ); - const prompt = testPrompt( - registry, - model, - `Hello {{name}}, how are you?` - ); - - const rendered = prompt.render({ input: { name: 'Michael' } }); - assert.deepStrictEqual(rendered.prompt, [ - { text: 'Hello Michael, how are you?' }, - ]); - }); - - it('should render default variables', () => { - const model = defineModel( - registry, - { name: 'echo', supports: { tools: true } }, - async (input) => ({ - message: input.messages[0], - finishReason: 'stop', - }) - ); - const prompt = testPrompt( - registry, - model, - `Hello {{name}}, how are you?`, - { - input: { default: { name: 'Fellow Human' } }, - } - ); - - const rendered = prompt.render({ input: {} }); - assert.deepStrictEqual(rendered.prompt, [ - { - text: 'Hello Fellow Human, how are you?', - }, - ]); - }); - - it('rejects input not matching the schema', async () => { - const invalidSchemaPrompt = defineDotprompt( - registry, - { - name: 'invalidInput', - model: 'echo', - input: { - jsonSchema: { - properties: { foo: { type: 'boolean' } }, - required: ['foo'], - }, - }, - }, - `You asked for {{foo}}.` - ); - - await assert.rejects(async () => { - invalidSchemaPrompt.render({ input: { foo: 'baz' } }); - }, ValidationError); - }); - - it('should render with overridden fields', () => { - const model = defineModel( - registry, - { name: 'echo', supports: { tools: true } }, - async (input) => ({ - message: input.messages[0], - finishReason: 'stop', - }) - ); - const prompt = testPrompt( - registry, - model, - `Hello {{name}}, how are you?` - ); - - const streamingCallback = (c) => console.log(c); - const middleware = [ - async (req, next) => { - return next(); - }, - ]; - - const rendered = prompt.render({ - input: { name: 'Michael' }, - onChunk: streamingCallback, - returnToolRequests: true, - toolChoice: 'required', - maxTurns: 17, - use: middleware, - }); - assert.strictEqual(rendered.onChunk, streamingCallback); - assert.strictEqual(rendered.returnToolRequests, true); - assert.strictEqual(rendered.toolChoice, 'required'); - assert.strictEqual(rendered.maxTurns, 17); - assert.deepStrictEqual(rendered.use, middleware); - }); - - it('should support system prompt with history', () => { - const model = defineModel( - registry, - { name: 'echo', supports: { tools: true } }, - async (input) => ({ - message: input.messages[0], - finishReason: 'stop', - }) - ); - const prompt = testPrompt( - registry, - model, - `{{ role "system" }}Testing system {{name}}` - ); - - const rendered = prompt.render({ - input: { name: 'Michael' }, - messages: [ - { role: 'user', content: [{ text: 'history 1' }] }, - { role: 'model', content: [{ text: 'history 2' }] }, - { role: 'user', content: [{ text: 'history 3' }] }, - ], - }); - assert.deepStrictEqual(rendered.messages, [ - { role: 'system', content: [{ text: 'Testing system Michael' }] }, - { role: 'user', content: [{ text: 'history 1' }] }, - { role: 'model', content: [{ text: 'history 2' }] }, - ]); - assert.deepStrictEqual(rendered.prompt, [{ text: 'history 3' }]); - }); - }); - - describe('#generate', () => { - it('renders and calls the model', async () => { - const model = defineModel( - registry, - { name: 'echo', supports: { tools: true } }, - async (input) => ({ - message: input.messages[0], - finishReason: 'stop', - }) - ); - const prompt = testPrompt( - registry, - model, - `Hello {{name}}, how are you?` - ); - const response = await prompt.generate({ input: { name: 'Bob' } }); - assert.equal(response.text, `Hello Bob, how are you?`); - }); - - it('rejects input not matching the schema', async () => { - const invalidSchemaPrompt = defineDotprompt( - registry, - { - name: 'invalidInput', - model: 'echo', - input: { - jsonSchema: { - properties: { foo: { type: 'boolean' } }, - required: ['foo'], - }, - }, - }, - `You asked for {{foo}}.` - ); - - await assert.rejects(async () => { - await invalidSchemaPrompt.generate({ input: { foo: 'baz' } }); - }, ValidationError); - }); - }); - - describe('#toJSON', () => { - it('should convert zod to json schema', () => { - const schema = z.object({ name: z.string() }); - const model = defineModel( - registry, - { name: 'echo', supports: { tools: true } }, - async (input) => ({ - message: input.messages[0], - finishReason: 'stop', - }) - ); - const prompt = testPrompt(registry, model, `hello {{name}}`, { - input: { schema }, - }); - - assert.deepStrictEqual( - prompt.toJSON().input?.schema, - toJsonSchema({ schema }) - ); - }); - }); - - describe('.parse', () => { - it('should throw a good error for invalid YAML', () => { - assert.throws( - () => { - Dotprompt.parse( - registry, - 'example', - `--- -input: { - isInvalid: true - wasInvalid: true -} ---- - -This is the rest of the prompt` - ); - }, - (e: any) => e.status === 'INVALID_ARGUMENT' - ); - }); - - it('should parse picoschema', () => { - const p = Dotprompt.parse( - registry, - 'example', - `--- -input: - schema: - type: string -output: - schema: - name: string, the name of the person - date?: string, ISO date like '2024-04-09' ----` - ); - - assert.deepEqual(p.input, { jsonSchema: { type: 'string' } }); - assert.deepEqual(p.output, { - jsonSchema: { - type: 'object', - required: ['name'], - additionalProperties: false, - properties: { - name: { type: 'string', description: 'the name of the person' }, - date: { - type: ['string', 'null'], - description: "ISO date like '2024-04-09'", - }, - }, - }, - }); - }); - - it('should use registered schemas', () => { - const MyInput = defineSchema(registry, 'MyInput', z.number()); - defineJsonSchema(registry, 'MyOutput', { type: 'boolean' }); - - const p = Dotprompt.parse( - registry, - 'example2', - `--- -input: - schema: MyInput -output: - schema: MyOutput ----` - ); - - assert.deepEqual(p.input, { schema: MyInput }); - assert.deepEqual(p.output, { jsonSchema: { type: 'boolean' } }); - }); - }); - - describe('defineDotprompt', () => { - it('registers a prompt and its variant', async () => { - defineDotprompt( - registry, - { - name: 'promptName', - model: 'echo', - }, - `This is a prompt.` - ); - - defineDotprompt( - registry, - { - name: 'promptName', - variant: 'variantName', - model: 'echo', - }, - `And this is its variant.` - ); - - const basePrompt = await prompt(registry, 'promptName'); - assert.equal('This is a prompt.', basePrompt.template); - - const variantPrompt = await prompt(registry, 'promptName', { - variant: 'variantName', - }); - assert.equal('And this is its variant.', variantPrompt.template); - }); - }); -}); - -describe('DotpromptRef', () => { - let registry: Registry; - beforeEach(() => { - registry = new Registry(); - }); - - it('Should load a prompt correctly', async () => { - defineDotprompt( - registry, - { - name: 'promptName', - model: 'echo', - }, - `This is a prompt.` - ); - - const ref = promptRef('promptName'); - - const p = await ref.loadPrompt(registry); - - const isDotprompt = p instanceof Dotprompt; - - assert.equal(isDotprompt, true); - assert.equal(p.template, 'This is a prompt.'); - }); - - it('Should generate output correctly using DotpromptRef', async () => { - const model = defineModel( - registry, - { name: 'echo', supports: { tools: true } }, - async (input) => ({ - message: input.messages[0], - finishReason: 'stop', - }) - ); - defineDotprompt( - registry, - { - name: 'generatePrompt', - model: 'echo', - }, - `Hello {{name}}, this is a test prompt.` - ); - - const ref = promptRef('generatePrompt'); - const response = await ref.generate(registry, { input: { name: 'Alice' } }); - - assert.equal(response.text, 'Hello Alice, this is a test prompt.'); - }); - - it('Should render correctly using DotpromptRef', async () => { - defineDotprompt( - registry, - { - name: 'renderPrompt', - model: 'echo', - }, - `Hi {{name}}, welcome to the system.` - ); - - const ref = promptRef('renderPrompt'); - const rendered = await ref.render(registry, { input: { name: 'Bob' } }); - - assert.deepStrictEqual(rendered.prompt, [ - { text: 'Hi Bob, welcome to the system.' }, - ]); - }); - - it('Should handle invalid schema input in DotpromptRef', async () => { - defineDotprompt( - registry, - { - name: 'invalidSchemaPromptRef', - model: 'echo', - input: { - jsonSchema: { - properties: { foo: { type: 'boolean' } }, - required: ['foo'], - }, - }, - }, - `This is the prompt with foo={{foo}}.` - ); - - const ref = promptRef('invalidSchemaPromptRef'); - - await assert.rejects(async () => { - await ref.generate(registry, { input: { foo: 'not_a_boolean' } }); - }, ValidationError); - }); - - it('Should support streamingCallback in DotpromptRef', async () => { - defineDotprompt( - registry, - { - name: 'streamingCallbackPrompt', - model: 'echo', - }, - `Hello {{name}}, streaming test.` - ); - - const ref = promptRef('streamingCallbackPrompt'); - - const streamingCallback = (chunk) => console.log(chunk); - const options = { - input: { name: 'Charlie' }, - onChunk: streamingCallback, - returnToolRequests: true, - maxTurns: 17, - }; - - const rendered = await ref.render(registry, options); - - assert.strictEqual(rendered.onChunk, streamingCallback); - assert.strictEqual(rendered.returnToolRequests, true); - assert.strictEqual(rendered.maxTurns, 17); - }); - - it('Should cache loaded prompt in DotpromptRef', async () => { - defineDotprompt( - registry, - { - name: 'cacheTestPrompt', - model: 'echo', - }, - `This is a prompt for cache test.` - ); - - const ref = promptRef('cacheTestPrompt'); - const firstLoad = await ref.loadPrompt(registry); - const secondLoad = await ref.loadPrompt(registry); - - assert.strictEqual( - firstLoad, - secondLoad, - 'Loaded prompts should be identical (cached).' - ); - }); - - it('should render system prompt', () => { - const model = defineModel( - registry, - { name: 'echo', supports: { tools: true } }, - async (input) => ({ - message: input.messages[0], - finishReason: 'stop', - }) - ); - const prompt = testPrompt(registry, model, `{{ role "system"}} hi`); - - const rendered = prompt.render({ input: {} }); - assert.deepStrictEqual(rendered.messages, [ - { - content: [{ text: ' hi' }], - role: 'system', - }, - ]); - }); - - it('should render system prompt', () => { - const model = defineModel( - registry, - { name: 'echo', supports: { tools: true } }, - async (input) => ({ - message: input.messages[0], - finishReason: 'stop', - }) - ); - const prompt = testPrompt(registry, model, `hi`, { - toolChoice: 'required', - }); - - const rendered = prompt.render({ input: {} }); - assert.deepStrictEqual(rendered.toolChoice, 'required'); - }); -}); diff --git a/js/plugins/dotprompt/tests/template_test.ts b/js/plugins/dotprompt/tests/template_test.ts deleted file mode 100644 index 5df3791af..000000000 --- a/js/plugins/dotprompt/tests/template_test.ts +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Copyright 2024 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 { MessageData } from '@genkit-ai/ai/model'; -import { DocumentData } from '@genkit-ai/ai/retriever'; -import * as assert from 'assert'; -import { describe, it } from 'node:test'; -import { compile } from '../src/template'; - -describe('compile', () => { - for (const test of [ - { - should: 'inject variables', - template: 'Hello {{name}}', - input: { name: 'World' }, - want: [{ role: 'user', content: [{ text: 'Hello World' }] }], - }, - { - should: 'allow multipart with url', - template: '{{media url=image}} Describe the image above.', - input: { image: 'https://some.image.url/image.jpg' }, - want: [ - { - role: 'user', - content: [ - { media: { url: 'https://some.image.url/image.jpg' } }, - { text: ' Describe the image above.' }, - ], - }, - ], - }, - { - should: 'allow multiple media parts, adjacent or separated by text', - template: - 'Look at these images: {{#each images}}{{media url=.}} {{/each}} Do you like them? Here ' + - 'is another: {{media url=anotherImage}}', - input: { - images: [ - 'http://1.png', - 'https://2.png', - 'data:image/jpeg;base64,abc123', - ], - anotherImage: 'http://anotherImage.png', - }, - want: [ - { - role: 'user', - content: [ - { - text: 'Look at these images: ', - }, - { - media: { url: 'http://1.png' }, - }, - { - media: { url: 'https://2.png' }, - }, - { - media: { url: 'data:image/jpeg;base64,abc123' }, - }, - { - text: ' Do you like them? Here is another: ', - }, - { - media: { url: 'http://anotherImage.png' }, - }, - ], - }, - ], - }, - { - should: 'allow changing the role at the beginning', - template: ` {{role "system"}}You are super helpful. - {{~role "user"}}Do something!`, - want: [ - { - role: 'system', - content: [{ text: 'You are super helpful.' }], - }, - { - role: 'user', - content: [{ text: 'Do something!' }], - }, - ], - }, - { - should: 'allow rendering JSON', - input: { test: true }, - template: '{{json .}}', - want: [{ role: 'user', content: [{ text: '{"test":true}' }] }], - }, - { - should: 'allow indenting JSON', - input: { test: true }, - template: '{{json . indent=2}}', - want: [{ role: 'user', content: [{ text: '{\n "test": true\n}' }] }], - }, - { - should: 'insert history after other messages before final user message', - input: {}, - template: `{{role "system"}}You are a robot{{role "user"}}Hello there`, - options: { - history: [ - { role: 'user', content: [{ text: 'Ping' }] }, - { role: 'model', content: [{ text: 'Pong' }] }, - ], - }, - want: [ - { role: 'system', content: [{ text: 'You are a robot' }] }, - { role: 'user', content: [{ text: 'Ping' }] }, - { role: 'model', content: [{ text: 'Pong' }] }, - { role: 'user', content: [{ text: 'Hello there' }] }, - ], - }, - { - should: 'insert history in the specified location', - input: {}, - template: `{{history}}{{role "system"}}You are a robot{{role "user"}}Hello there`, - options: { - history: [ - { role: 'user', content: [{ text: 'Ping' }] }, - { role: 'model', content: [{ text: 'Pong' }] }, - ], - }, - want: [ - { - role: 'user', - content: [{ text: 'Ping' }], - metadata: { purpose: 'history' }, - }, - { - role: 'model', - content: [{ text: 'Pong' }], - metadata: { purpose: 'history' }, - }, - { role: 'system', content: [{ text: 'You are a robot' }] }, - { role: 'user', content: [{ text: 'Hello there' }] }, - ], - }, - { - should: 'insert a blank context section when helper provided', - input: {}, - template: `before{{section "context"}}after`, - options: { - context: [{ content: [{ text: 'doc content' }] }], - }, - want: [ - { - role: 'user', - content: [ - { text: 'before' }, - { metadata: { purpose: 'context', pending: true } }, - { text: 'after' }, - ], - }, - ], - }, - { - should: 'insert a blank output section when helper provided', - input: {}, - template: `before{{section "output"}}after`, - options: { - context: [{ content: [{ text: 'doc content' }] }], - }, - want: [ - { - role: 'user', - content: [ - { text: 'before' }, - { metadata: { purpose: 'output', pending: true } }, - { text: 'after' }, - ], - }, - ], - }, - ]) { - it(test.should, () => { - assert.deepEqual( - compile(test.template, { model: 'test/example' })(test.input, { - messages: test.options?.history as MessageData[], - context: test.options?.context as DocumentData[], - }), - test.want - ); - }); - } -}); diff --git a/js/plugins/dotprompt/tsconfig.json b/js/plugins/dotprompt/tsconfig.json deleted file mode 100644 index 596e2cf72..000000000 --- a/js/plugins/dotprompt/tsconfig.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "include": ["src"] -} diff --git a/js/plugins/dotprompt/tsup.config.ts b/js/plugins/dotprompt/tsup.config.ts deleted file mode 100644 index 01dce0a6b..000000000 --- a/js/plugins/dotprompt/tsup.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright 2024 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 { defineConfig, Options } from 'tsup'; -import { defaultOptions } from '../../tsup.common'; - -export default defineConfig({ - ...(defaultOptions as Options), -}); diff --git a/js/plugins/dotprompt/typedoc.json b/js/plugins/dotprompt/typedoc.json deleted file mode 100644 index 35fed2c95..000000000 --- a/js/plugins/dotprompt/typedoc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "entryPoints": ["src/index.ts"] -} diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 2bcc01d55..764f99b1f 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -30,10 +30,10 @@ "author": "genkit", "license": "Apache-2.0", "dependencies": { - "@genkit-ai/dotprompt": "workspace:*", "compute-cosine-similarity": "^1.1.0", "node-fetch": "^3.3.2", - "path": "^0.12.7" + "path": "^0.12.7", + "dotprompt": "^1.0.0-dev || ^1" }, "peerDependencies": { "genkit": "workspace:*" diff --git a/js/plugins/evaluators/src/metrics/answer_relevancy.ts b/js/plugins/evaluators/src/metrics/answer_relevancy.ts index e8d101d6f..945ee5b0c 100644 --- a/js/plugins/evaluators/src/metrics/answer_relevancy.ts +++ b/js/plugins/evaluators/src/metrics/answer_relevancy.ts @@ -14,13 +14,12 @@ * limitations under the License. */ -import { loadPromptFile } from '@genkit-ai/dotprompt'; import similarity from 'compute-cosine-similarity'; import { Genkit, ModelArgument, z } from 'genkit'; import { EmbedderArgument } from 'genkit/embedder'; import { BaseEvalDataPoint, Score } from 'genkit/evaluator'; import path from 'path'; -import { getDirName } from './helper.js'; +import { getDirName, loadPromptFile, renderText } from './helper.js'; const AnswerRelevancyResponseSchema = z.object({ question: z.string(), @@ -61,13 +60,12 @@ export async function answerRelevancyScore< const context = dataPoint.context.map((i) => JSON.stringify(i)); const prompt = await loadPromptFile( - ai.registry, path.resolve(getDirName(), '../../prompts/answer_relevancy.prompt') ); const response = await ai.generate({ model: judgeLlm, config: judgeConfig, - prompt: prompt.renderText({ + prompt: await renderText(prompt, { question: input, answer: output, context: context.join(' '), diff --git a/js/plugins/evaluators/src/metrics/faithfulness.ts b/js/plugins/evaluators/src/metrics/faithfulness.ts index 3ee97664c..08a658c7a 100644 --- a/js/plugins/evaluators/src/metrics/faithfulness.ts +++ b/js/plugins/evaluators/src/metrics/faithfulness.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import { loadPromptFile } from '@genkit-ai/dotprompt'; import { Genkit, ModelArgument, z } from 'genkit'; import { BaseEvalDataPoint, Score } from 'genkit/evaluator'; import path from 'path'; -import { getDirName } from './helper.js'; +import { getDirName, loadPromptFile, renderText } from './helper.js'; const LongFormResponseSchema = z.object({ statements: z.array(z.string()) }); @@ -64,13 +63,12 @@ export async function faithfulnessScore< const context = dataPoint.context.map((i) => JSON.stringify(i)); const longFormPrompt = await loadPromptFile( - ai.registry, path.resolve(getDirName(), '../../prompts/faithfulness_long_form.prompt') ); const longFormResponse = await ai.generate({ model: judgeLlm, config: judgeConfig, - prompt: longFormPrompt.renderText({ + prompt: await renderText(longFormPrompt, { question: input, answer: output, }), @@ -86,12 +84,11 @@ export async function faithfulnessScore< const allStatements = statements.map((s) => `statement: ${s}`).join('\n'); const allContext = context.join('\n'); const nliPrompt = await loadPromptFile( - ai.registry, path.resolve(getDirName(), '../../prompts/faithfulness_nli.prompt') ); const response = await ai.generate({ model: judgeLlm, - prompt: nliPrompt.renderText({ + prompt: await renderText(nliPrompt, { context: allContext, statements: allStatements, }), diff --git a/js/plugins/evaluators/src/metrics/helper.ts b/js/plugins/evaluators/src/metrics/helper.ts index dd94bedf7..ec59c2fc4 100644 --- a/js/plugins/evaluators/src/metrics/helper.ts +++ b/js/plugins/evaluators/src/metrics/helper.ts @@ -14,7 +14,28 @@ * limitations under the License. */ +import { dotprompt, PromptFunction } from 'dotprompt'; +import { readFileSync } from 'fs'; + /** Helper function to get current directory, isolated in a separate file to work with ESM */ export function getDirName() { return __dirname; } + +const dp = dotprompt(); + +export function loadPromptFile(path: string): Promise { + return dp.compile(dp.parse(readFileSync(path, 'utf8'))); +} + +export async function renderText( + prompt: PromptFunction, + input +): Promise { + const renderred = await prompt({ + input, + }); + return renderred.messages + .map((m) => m.content.map((c) => c.text).join()) + .join(); +} diff --git a/js/plugins/evaluators/src/metrics/maliciousness.ts b/js/plugins/evaluators/src/metrics/maliciousness.ts index 92716a2fd..6bf38bfa2 100644 --- a/js/plugins/evaluators/src/metrics/maliciousness.ts +++ b/js/plugins/evaluators/src/metrics/maliciousness.ts @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { loadPromptFile } from '@genkit-ai/dotprompt'; import { Genkit, ModelArgument, z } from 'genkit'; import { BaseEvalDataPoint, Score } from 'genkit/evaluator'; import path from 'path'; -import { getDirName } from './helper.js'; +import { getDirName, loadPromptFile, renderText } from './helper.js'; const MaliciousnessResponseSchema = z.object({ reason: z.string(), @@ -50,14 +49,13 @@ export async function maliciousnessScore< : JSON.stringify(dataPoint.output); const prompt = await loadPromptFile( - ai.registry, path.resolve(getDirName(), '../../prompts/maliciousness.prompt') ); //TODO: safetySettings are gemini specific - pull these out so they are tied to the LLM const response = await ai.generate({ model: judgeLlm, config: judgeConfig, - prompt: prompt.renderText({ + prompt: await renderText(prompt, { input: input, submission: output, }), diff --git a/js/plugins/mcp/src/client/prompts.ts b/js/plugins/mcp/src/client/prompts.ts index f322473d6..fb7ef601c 100644 --- a/js/plugins/mcp/src/client/prompts.ts +++ b/js/plugins/mcp/src/client/prompts.ts @@ -43,14 +43,12 @@ function registerPrompt( logger.debug( `[@genkit-ai/mcp] Registering MCP prompt ${params.name}/${prompt.name}` ); - ai.definePrompt( - { - name: prompt.name, - description: prompt.description || '', - input: { jsonSchema: toSchema(prompt.arguments) }, - output: { format: 'text' }, - }, - async (args) => { + ai.definePrompt({ + name: prompt.name, + description: prompt.description || '', + input: { jsonSchema: toSchema(prompt.arguments) }, + output: { format: 'text' }, + messages: async (args) => { logger.debug( `[@genkit-ai/mcp] Calling MCP prompt ${params.name}/${prompt.name} with arguments`, JSON.stringify(args) @@ -59,11 +57,9 @@ function registerPrompt( name: prompt.name, arguments: args, }); - return { - messages: result.messages.map(fromMcpPromptMessage), - }; - } - ); + return result.messages.map(fromMcpPromptMessage); + }, + }); } /** diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 42a12bba8..60faabe3b 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -50,6 +50,9 @@ importers: colorette: specifier: ^2.0.20 version: 2.0.20 + dotprompt: + specifier: ^1.0.0-dev || ^1 + version: 1.0.0-dev.2 json5: specifier: ^2.2.3 version: 2.2.3 @@ -117,6 +120,9 @@ importers: cors: specifier: ^2.8.5 version: 2.8.5 + dotprompt: + specifier: ^1.0.0-dev || ^1 + version: 1.0.0-dev.2 express: specifier: ^4.21.0 version: 4.21.0 @@ -200,9 +206,6 @@ importers: '@genkit-ai/core': specifier: workspace:* version: link:../core - '@genkit-ai/dotprompt': - specifier: workspace:* - version: link:../plugins/dotprompt uuid: specifier: ^10.0.0 version: 10.0.0 @@ -328,54 +331,14 @@ importers: specifier: ^4.9.0 version: 4.9.5 - plugins/dotprompt: - dependencies: - '@genkit-ai/ai': - specifier: workspace:* - version: link:../../ai - '@genkit-ai/core': - specifier: workspace:* - version: link:../../core - front-matter: - specifier: ^4.0.2 - version: 4.0.2 - handlebars: - specifier: ^4.7.8 - version: 4.7.8 - node-fetch: - specifier: ^3.3.2 - version: 3.3.2 - devDependencies: - '@types/node': - specifier: ^20.11.16 - version: 20.11.30 - npm-run-all: - specifier: ^4.1.5 - version: 4.1.5 - rimraf: - specifier: ^6.0.1 - version: 6.0.1 - tsup: - specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.4.1) - tsx: - specifier: ^4.19.2 - version: 4.19.2 - typescript: - specifier: ^4.9.0 - version: 4.9.5 - yaml: - specifier: ^2.4.1 - version: 2.4.1 - plugins/evaluators: dependencies: - '@genkit-ai/dotprompt': - specifier: workspace:* - version: link:../dotprompt compute-cosine-similarity: specifier: ^1.1.0 version: 1.1.0 + dotprompt: + specifier: ^1.0.0-dev || ^1 + version: 1.0.0-dev.2 genkit: specifier: workspace:* version: link:../../genkit @@ -1329,9 +1292,6 @@ importers: '@genkit-ai/dev-local-vectorstore': specifier: workspace:* version: link:../../plugins/dev-local-vectorstore - '@genkit-ai/dotprompt': - specifier: workspace:* - version: link:../../plugins/dotprompt '@genkit-ai/evaluator': specifier: workspace:* version: link:../../plugins/evaluators @@ -1374,7 +1334,7 @@ importers: version: link:../../plugins/ollama genkitx-openai: specifier: ^0.10.1 - version: 0.10.1(@genkit-ai/ai@1.0.0-rc.2)(@genkit-ai/core@1.0.0-rc.2) + version: 0.10.1(@genkit-ai/ai@1.0.0-rc.5)(@genkit-ai/core@1.0.0-rc.5) devDependencies: rimraf: specifier: ^6.0.1 @@ -2405,11 +2365,11 @@ packages: '@firebase/webchannel-wrapper@1.0.3': resolution: {integrity: sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==} - '@genkit-ai/ai@1.0.0-rc.2': - resolution: {integrity: sha512-pAyjFtfw0pv9bcQHy3b40gcMUx7FL3vbdDIYvfbbizPEq+/k9DGk/HQwYI1B490xm2On/AXIvYtCo7/aTEAIcw==} + '@genkit-ai/ai@1.0.0-rc.5': + resolution: {integrity: sha512-bNalrwA5HTNa9lo0dbh6I/fCFMbiqMXo1GYNwWbdYEZFyaK11L1sgoAcRoafZQJublvFKnaTn9llrn7VHQkaBA==} - '@genkit-ai/core@1.0.0-rc.2': - resolution: {integrity: sha512-wnrK0ZOt+VguN71kTo9/DdPchofIadKvpyG1UOzuHkc/SCMWkHzP8yHneh1clZ/uskPDR5OzrSBaQ2P0i8fwnw==} + '@genkit-ai/core@1.0.0-rc.5': + resolution: {integrity: sha512-jTu0Flixq27H3dKAwysoUHk5LwX0anMevd2J+0reTK0DCRzVdc0mJjlBw5nX29XszB9zAp+Y/bSOjeCreBW33A==} '@gerrit0/mini-shiki@1.24.4': resolution: {integrity: sha512-YEHW1QeAg6UmxEmswiQbOVEg1CW22b1XUD/lNTliOsu0LD0wqoyleFMnmbTp697QE0pcadQiR5cVtbbAPncvpw==} @@ -4249,6 +4209,9 @@ packages: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} + dotprompt@1.0.0-dev.2: + resolution: {integrity: sha512-RcpdKpKd1XemHpb6Lti/ekLGs+9or+11n/aUSZ9e8QM1qTNFUloa9bdqg4OJDvpAlihv34uCt8yUlV2h4MglqA==} + duplexify@4.1.3: resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} @@ -4521,9 +4484,6 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} - front-matter@4.0.2: - resolution: {integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==} - fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -7466,9 +7426,9 @@ snapshots: '@firebase/webchannel-wrapper@1.0.3': {} - '@genkit-ai/ai@1.0.0-rc.2': + '@genkit-ai/ai@1.0.0-rc.5': dependencies: - '@genkit-ai/core': 1.0.0-rc.2 + '@genkit-ai/core': 1.0.0-rc.5 '@opentelemetry/api': 1.9.0 '@types/node': 20.16.9 colorette: 2.0.20 @@ -7479,7 +7439,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@genkit-ai/core@1.0.0-rc.2': + '@genkit-ai/core@1.0.0-rc.5': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) @@ -7487,6 +7447,7 @@ snapshots: '@opentelemetry/sdk-metrics': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-node': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 1.26.0(@opentelemetry/api@1.9.0) + '@types/json-schema': 7.0.15 ajv: 8.12.0 ajv-formats: 3.0.1(ajv@8.12.0) async-mutex: 0.5.0 @@ -9553,6 +9514,11 @@ snapshots: dotenv@16.4.5: {} + dotprompt@1.0.0-dev.2: + dependencies: + handlebars: 4.7.8 + yaml: 2.6.1 + duplexify@4.1.3: dependencies: end-of-stream: 1.4.4 @@ -10016,10 +9982,6 @@ snapshots: fresh@0.5.2: {} - front-matter@4.0.2: - dependencies: - js-yaml: 3.14.1 - fs-constants@1.0.0: optional: true @@ -10079,10 +10041,10 @@ snapshots: - encoding - supports-color - genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-rc.2)(@genkit-ai/core@1.0.0-rc.2): + genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-rc.5)(@genkit-ai/core@1.0.0-rc.5): dependencies: - '@genkit-ai/ai': 1.0.0-rc.2 - '@genkit-ai/core': 1.0.0-rc.2 + '@genkit-ai/ai': 1.0.0-rc.5 + '@genkit-ai/core': 1.0.0-rc.5 openai: 4.53.0(encoding@0.1.13) zod: 3.23.8 transitivePeerDependencies: @@ -11664,14 +11626,6 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-load-config@6.0.1(postcss@8.4.47)(tsx@4.19.2)(yaml@2.4.1): - dependencies: - lilconfig: 3.1.2 - optionalDependencies: - postcss: 8.4.47 - tsx: 4.19.2 - yaml: 2.4.1 - postcss-load-config@6.0.1(postcss@8.4.47)(tsx@4.19.2)(yaml@2.6.1): dependencies: lilconfig: 3.1.2 @@ -12279,7 +12233,6 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.25.7) - esbuild: 0.24.0 ts-jest@29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)))(typescript@5.6.3): dependencies: @@ -12342,33 +12295,6 @@ snapshots: tslib@2.6.2: {} - tsup@8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.4.1): - dependencies: - bundle-require: 5.0.0(esbuild@0.24.0) - cac: 6.7.14 - chokidar: 4.0.1 - consola: 3.2.3 - debug: 4.3.7 - esbuild: 0.24.0 - joycon: 3.1.1 - picocolors: 1.1.1 - postcss-load-config: 6.0.1(postcss@8.4.47)(tsx@4.19.2)(yaml@2.4.1) - resolve-from: 5.0.0 - rollup: 4.25.0 - source-map: 0.8.0-beta.0 - sucrase: 3.35.0 - tinyexec: 0.3.1 - tinyglobby: 0.2.10 - tree-kill: 1.2.2 - optionalDependencies: - postcss: 8.4.47 - typescript: 4.9.5 - transitivePeerDependencies: - - jiti - - supports-color - - tsx - - yaml - tsup@8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1): dependencies: bundle-require: 5.0.0(esbuild@0.24.0) diff --git a/js/testapps/custom-evaluators/src/deliciousness/deliciousness.ts b/js/testapps/custom-evaluators/src/deliciousness/deliciousness.ts index 1c887d50d..84fe1de35 100644 --- a/js/testapps/custom-evaluators/src/deliciousness/deliciousness.ts +++ b/js/testapps/custom-evaluators/src/deliciousness/deliciousness.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { ModelArgument, loadPromptFile, z } from 'genkit'; +import { ModelArgument, z } from 'genkit'; import { BaseEvalDataPoint, Score } from 'genkit/evaluator'; -import path from 'path'; import { ai } from '../index.js'; const DELICIOUSNESS_VALUES = ['yes', 'no', 'maybe'] as const; @@ -27,6 +26,8 @@ const DeliciousnessDetectionResponseSchema = z.object({ verdict: z.enum(DELICIOUSNESS_VALUES), }); +const deliciousnessPrompt = ai.prompt('deliciousness'); + export async function deliciousnessScore< CustomModelOptions extends z.ZodTypeAny, >( @@ -39,15 +40,16 @@ export async function deliciousnessScore< if (!d.output) { throw new Error('Output is required for Funniness detection'); } - const finalPrompt = await loadPromptFile( - ai.registry, - path.resolve(__dirname, '../../prompts/deliciousness.prompt') - ); + const finalPrompt = ( + await deliciousnessPrompt.render({ + output: d.output as string, + }) + ).messages + ?.map((m) => m.content.map((c) => c.text).join()) + .join(); const response = await ai.generate({ model: judgeLlm, - prompt: finalPrompt.renderText({ - output: d.output as string, - }), + prompt: finalPrompt, config: judgeConfig, output: { schema: DeliciousnessDetectionResponseSchema, diff --git a/js/testapps/custom-evaluators/src/funniness/funniness.ts b/js/testapps/custom-evaluators/src/funniness/funniness.ts index e1a1df5cf..8e2d2aa84 100644 --- a/js/testapps/custom-evaluators/src/funniness/funniness.ts +++ b/js/testapps/custom-evaluators/src/funniness/funniness.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { ModelArgument, loadPromptFile, z } from 'genkit'; +import { ModelArgument, z } from 'genkit'; import { BaseEvalDataPoint, Score } from 'genkit/evaluator'; -import path from 'path'; import { ai } from '../index.js'; const FUNNINESS_VALUES = [ @@ -26,6 +25,8 @@ const FUNNINESS_VALUES = [ 'NOT_A_JOKE', ] as const; +const funninessPrompt = ai.prompt('funniness'); + const FunninessResponseSchema = z.object({ reason: z.string(), verdict: z.enum(FUNNINESS_VALUES), @@ -41,16 +42,17 @@ export async function funninessScore( if (!d.output) { throw new Error('Output is required for Funniness detection'); } - const finalPrompt = await loadPromptFile( - ai.registry, - path.resolve(__dirname, '../../prompts/funniness.prompt') - ); + const finalPrompt = ( + await funninessPrompt.render({ + output: d.output as string, + }) + ).messages + ?.map((m) => m.content.map((c) => c.text).join()) + .join(); const response = await ai.generate({ model: judgeLlm, - prompt: finalPrompt.renderText({ - output: d.output as string, - }), + prompt: finalPrompt, config: judgeConfig, output: { schema: FunninessResponseSchema, diff --git a/js/testapps/custom-evaluators/src/pii/pii_detection.ts b/js/testapps/custom-evaluators/src/pii/pii_detection.ts index d0079fdd1..cedd120c3 100644 --- a/js/testapps/custom-evaluators/src/pii/pii_detection.ts +++ b/js/testapps/custom-evaluators/src/pii/pii_detection.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { ModelArgument, loadPromptFile, z } from 'genkit'; +import { ModelArgument, z } from 'genkit'; import { BaseEvalDataPoint, Score } from 'genkit/evaluator'; -import path from 'path'; import { ai } from '../index.js'; const PiiDetectionResponseSchema = z.object({ @@ -24,6 +23,8 @@ const PiiDetectionResponseSchema = z.object({ verdict: z.boolean(), }); +const piiDetectionPrompt = ai.prompt('pii_detection'); + export async function piiDetectionScore< CustomModelOptions extends z.ZodTypeAny, >( @@ -36,16 +37,18 @@ export async function piiDetectionScore< if (!d.output) { throw new Error('Output is required for PII detection'); } - const finalPrompt = await loadPromptFile( - ai.registry, - path.resolve(__dirname, '../../prompts/pii_detection.prompt') - ); + + const finalPrompt = ( + await piiDetectionPrompt.render({ + output: d.output as string, + }) + ).messages + ?.map((m) => m.content.map((c) => c.text).join()) + .join(); const response = await ai.generate({ model: judgeLlm, - prompt: finalPrompt.renderText({ - output: d.output as string, - }), + prompt: finalPrompt, config: judgeConfig, output: { schema: PiiDetectionResponseSchema, diff --git a/js/testapps/dev-ui-gallery/src/main/prompts.ts b/js/testapps/dev-ui-gallery/src/main/prompts.ts index 6c50672fb..7a5fb40a1 100644 --- a/js/testapps/dev-ui-gallery/src/main/prompts.ts +++ b/js/testapps/dev-ui-gallery/src/main/prompts.ts @@ -26,68 +26,58 @@ import { ai } from '../genkit.js'; const promptName = 'codeDefinedPrompt'; const template = 'Say hello to {{name}} in the voice of a {{persona}}.'; -export const codeDefinedPrompt = ai.definePrompt( - { - name: promptName, - model: gemini15Flash, - input: { - schema: HelloSchema, - default: { - persona: 'Space Pirate', - }, - }, - output: { - format: 'text', - }, - config: { - maxOutputTokens: 2048, - temperature: 0.6, - topK: 16, - topP: 0.95, - stopSequences: ['STAWP!'], - safetySettings: [ - { - category: 'HARM_CATEGORY_HATE_SPEECH', - threshold: 'BLOCK_ONLY_HIGH', - }, - { - category: 'HARM_CATEGORY_DANGEROUS_CONTENT', - threshold: 'BLOCK_ONLY_HIGH', - }, - { - category: 'HARM_CATEGORY_HARASSMENT', - threshold: 'BLOCK_ONLY_HIGH', - }, - { - category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', - threshold: 'BLOCK_ONLY_HIGH', - }, - ], - }, +export const codeDefinedPrompt = ai.definePrompt({ + name: promptName, + model: gemini15Flash, + input: { + schema: HelloSchema, }, - template -); - -export const codeDefinedPromptVariant = ai.definePrompt( - { - name: promptName, - variant: 'jsonOutput', - model: gemini15Flash, - input: { - schema: HelloSchema, - default: { - persona: 'Sportscaster', + output: { + format: 'text', + }, + config: { + maxOutputTokens: 2048, + temperature: 0.6, + topK: 16, + topP: 0.95, + stopSequences: ['STAWP!'], + safetySettings: [ + { + category: 'HARM_CATEGORY_HATE_SPEECH', + threshold: 'BLOCK_ONLY_HIGH', }, - }, - output: { - schema: z.object({ - greeting: z.string(), - }), - format: 'json', - }, + { + category: 'HARM_CATEGORY_DANGEROUS_CONTENT', + threshold: 'BLOCK_ONLY_HIGH', + }, + { + category: 'HARM_CATEGORY_HARASSMENT', + threshold: 'BLOCK_ONLY_HIGH', + }, + { + category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT', + threshold: 'BLOCK_ONLY_HIGH', + }, + ], }, - template -); + prompt: template, +}); + +export const codeDefinedPromptVariant = ai.definePrompt({ + name: promptName, + variant: 'jsonOutput', + model: gemini15Flash, + input: { + schema: HelloSchema, + }, + output: { + schema: z.object({ + greeting: z.string(), + }), + format: 'json', + }, + prompt: template, +}); ai.defineFlow( { @@ -105,30 +95,23 @@ ai.defineFlow( // Function(al) prompts // -export const promptFn = ai.definePrompt( - { - name: 'functionalPrompt', - input: { - schema: HelloSchema, - default: { - persona: 'Space Pirate', - }, - }, - model: gemini15Flash, +export const promptFn = ai.definePrompt({ + name: 'functionalPrompt', + input: { + schema: HelloSchema, }, - async (input) => ({ - messages: [ - { - role: 'user', - content: [ - { - text: `say hello to ${input.name} in the voice of ${input.persona}`, - }, - ], - }, - ], - }) -); + model: gemini15Flash, + messages: async (input) => [ + { + role: 'user', + content: [ + { + text: `say hello to ${input.name} in the voice of ${input.persona}`, + }, + ], + }, + ], +}); ai.defineFlow( { diff --git a/js/testapps/dev-ui-gallery/src/main/tools.ts b/js/testapps/dev-ui-gallery/src/main/tools.ts index 21a42f8ad..f0b1eade6 100644 --- a/js/testapps/dev-ui-gallery/src/main/tools.ts +++ b/js/testapps/dev-ui-gallery/src/main/tools.ts @@ -62,29 +62,24 @@ const template = ` I want to be outside as much as possible. Here are the cities I am considering:\n\n{{#each cities}}{{this}}\n{{/each}}`; -export const weatherPrompt = ai.definePrompt( - { - name: 'weatherPrompt', - model: gemini15Flash, - input: { - schema: WeatherSchema, - default: { - persona: 'Space Pirate', - }, - }, - output: { - format: 'text', - }, - config: { - maxOutputTokens: 2048, - temperature: 0.6, - topK: 16, - topP: 0.95, - }, - tools: ['getWeather'], +export const weatherPrompt = ai.definePrompt({ + name: 'weatherPrompt', + model: gemini15Flash, + input: { + schema: WeatherSchema, }, - template -); + output: { + format: 'text', + }, + config: { + maxOutputTokens: 2048, + temperature: 0.6, + topK: 16, + topP: 0.95, + }, + tools: ['getWeather'], + messages: template, +}); ai.defineFlow( { diff --git a/js/testapps/flow-simple-ai/prompts/TellJoke.prompt b/js/testapps/flow-simple-ai/prompts/TellJoke.prompt index 15d49519a..1865390c7 100644 --- a/js/testapps/flow-simple-ai/prompts/TellJoke.prompt +++ b/js/testapps/flow-simple-ai/prompts/TellJoke.prompt @@ -1,5 +1,7 @@ --- -modelProvider: google-vertex -modelName: gemini-pro +model: googleai/gemini-1.5-flash +input: + schema: + subject: string --- Tell a joke about {subject} \ No newline at end of file diff --git a/js/testapps/menu/package.json b/js/testapps/menu/package.json index 5fe3ffe0e..7af5dd755 100644 --- a/js/testapps/menu/package.json +++ b/js/testapps/menu/package.json @@ -17,7 +17,6 @@ "license": "ISC", "dependencies": { "genkit": "workspace:*", - "@genkit-ai/dotprompt": "workspace:*", "@genkit-ai/dev-local-vectorstore": "workspace:*", "@genkit-ai/firebase": "workspace:*", "@genkit-ai/evaluator": "workspace:*", diff --git a/js/testapps/menu/src/01/prompts.ts b/js/testapps/menu/src/01/prompts.ts index b937ae477..0e67da323 100644 --- a/js/testapps/menu/src/01/prompts.ts +++ b/js/testapps/menu/src/01/prompts.ts @@ -15,46 +15,39 @@ */ import { gemini15Flash } from '@genkit-ai/vertexai'; -import { GenerateRequest } from 'genkit'; import { ai } from '../genkit.js'; import { MenuQuestionInput, MenuQuestionInputSchema } from '../types.js'; // Define a prompt to handle a customer question about the menu. // This prompt uses definePrompt directly. -export const s01_vanillaPrompt = ai.definePrompt( - { - name: 's01_vanillaPrompt', - input: { - schema: MenuQuestionInputSchema, - }, +export const s01_vanillaPrompt = ai.definePrompt({ + name: 's01_vanillaPrompt', + input: { + schema: MenuQuestionInputSchema, }, - async (input: MenuQuestionInput): Promise => { + config: { temperature: 0.3 }, + messages: async (input: MenuQuestionInput) => { const promptText = ` - You are acting as a helpful AI assistant named "Walt" that can answer - questions about the food available on the menu at Walt's Burgers. - Customer says: ${input.question} - `; + You are acting as a helpful AI assistant named "Walt" that can answer + questions about the food available on the menu at Walt's Burgers. + Customer says: ${input.question} + `; - return { - messages: [{ role: 'user', content: [{ text: promptText }] }], - config: { temperature: 0.3 }, - }; - } -); + return [{ role: 'user', content: [{ text: promptText }] }]; + }, +}); // Define another prompt which uses the Dotprompt library // that also gives us a type-safe handlebars template system, // and well-defined output schemas. -export const s01_staticMenuDotPrompt = ai.definePrompt( - { - name: 's01_staticMenuDotPrompt', - model: gemini15Flash, - input: { schema: MenuQuestionInputSchema }, - output: { format: 'text' }, - }, - ` +export const s01_staticMenuDotPrompt = ai.definePrompt({ + name: 's01_staticMenuDotPrompt', + model: gemini15Flash, + input: { schema: MenuQuestionInputSchema }, + output: { format: 'text' }, + messages: ` You are acting as a helpful AI assistant named "Walt" that can answer questions about the food available on the menu at Walt's Burgers. Here is today's menu: @@ -85,5 +78,5 @@ as long as it is about food. Question: {{question}} ? -` -); +`, +}); diff --git a/js/testapps/menu/src/02/prompts.ts b/js/testapps/menu/src/02/prompts.ts index a4250d314..ff2f45514 100644 --- a/js/testapps/menu/src/02/prompts.ts +++ b/js/testapps/menu/src/02/prompts.ts @@ -22,18 +22,16 @@ import { menuTool } from './tools.js'; // The prompt uses a tool which will load the menu data, // if the user asks a reasonable question about the menu. -export const s02_dataMenuPrompt = ai.definePrompt( - { - name: 's02_dataMenu', - model: gemini15Flash, - input: { schema: MenuQuestionInputSchema }, - output: { format: 'text' }, - tools: [menuTool], - config: { - temperature: 0.2, - }, +export const s02_dataMenuPrompt = ai.definePrompt({ + name: 's02_dataMenu', + model: gemini15Flash, + input: { schema: MenuQuestionInputSchema }, + output: { format: 'text' }, + tools: [menuTool], + config: { + temperature: 0.2, }, - ` + messages: ` You are acting as a helpful AI assistant named Walt that can answer questions about the food available on the menu at Walt's Burgers. @@ -44,5 +42,5 @@ DO NOT INVENT ITEMS NOT ON THE MENU. USE THE TOOL. Question: {{question}} ? -` -); +`, +}); diff --git a/js/testapps/menu/src/04/prompts.ts b/js/testapps/menu/src/04/prompts.ts index ef0c91427..088972708 100644 --- a/js/testapps/menu/src/04/prompts.ts +++ b/js/testapps/menu/src/04/prompts.ts @@ -18,15 +18,13 @@ import { gemini15Flash } from '@genkit-ai/vertexai'; import { ai } from '../genkit.js'; import { DataMenuQuestionInputSchema } from '../types.js'; -export const s04_ragDataMenuPrompt = ai.definePrompt( - { - name: 's04_ragDataMenu', - model: gemini15Flash, - input: { schema: DataMenuQuestionInputSchema }, - output: { format: 'text' }, - config: { temperature: 0.3 }, - }, - ` +export const s04_ragDataMenuPrompt = ai.definePrompt({ + name: 's04_ragDataMenu', + model: gemini15Flash, + input: { schema: DataMenuQuestionInputSchema }, + output: { format: 'text' }, + config: { temperature: 0.3 }, + messages: ` You are acting as Walt, a helpful AI assistant here at the restaurant. You can answer questions about the food on the menu or any other questions customers have about food in general. @@ -40,5 +38,5 @@ helping you answer the customer's question: Answer this customer's question: {{question}}? -` -); +`, +}); diff --git a/js/testapps/menu/src/05/prompts.ts b/js/testapps/menu/src/05/prompts.ts index 05f204962..8e697cbd0 100644 --- a/js/testapps/menu/src/05/prompts.ts +++ b/js/testapps/menu/src/05/prompts.ts @@ -19,35 +19,31 @@ import { z } from 'genkit'; import { ai } from '../genkit.js'; import { TextMenuQuestionInputSchema } from '../types.js'; -export const s05_readMenuPrompt = ai.definePrompt( - { - name: 's05_readMenu', - model: gemini15Flash, - input: { - schema: z.object({ - imageUrl: z.string(), - }), - }, - output: { format: 'text' }, - config: { temperature: 0.1 }, +export const s05_readMenuPrompt = ai.definePrompt({ + name: 's05_readMenu', + model: gemini15Flash, + input: { + schema: z.object({ + imageUrl: z.string(), + }), }, - ` + output: { format: 'text' }, + config: { temperature: 0.1 }, + messages: ` Extract _all_ of the text, in order, from the following image of a restaurant menu. {{media url=imageUrl}} -` -); +`, +}); -export const s05_textMenuPrompt = ai.definePrompt( - { - name: 's05_textMenu', - model: gemini15Flash, - input: { schema: TextMenuQuestionInputSchema }, - output: { format: 'text' }, - config: { temperature: 0.3 }, - }, - ` +export const s05_textMenuPrompt = ai.definePrompt({ + name: 's05_textMenu', + model: gemini15Flash, + input: { schema: TextMenuQuestionInputSchema }, + output: { format: 'text' }, + config: { temperature: 0.3 }, + messages: ` You are acting as Walt, a helpful AI assistant here at the restaurant. You can answer questions about the food on the menu or any other questions customers have about food in general. @@ -57,5 +53,5 @@ Here is the text of today's menu to help you answer the customer's question: Answer this customer's question: {{question}}? -` -); +`, +}); diff --git a/js/testapps/rag/src/prompt.ts b/js/testapps/rag/src/prompt.ts index d494de7f7..f93992937 100644 --- a/js/testapps/rag/src/prompt.ts +++ b/js/testapps/rag/src/prompt.ts @@ -20,21 +20,19 @@ import { ai } from './genkit.js'; // Define a prompt that includes the retrieved context documents -export const augmentedPrompt = ai.definePrompt( - { - model: gemini15Flash, - name: 'augmentedPrompt', - input: { - schema: z.object({ - context: z.array(z.string()), - question: z.string(), - }), - }, - output: { - format: 'text', - }, +export const augmentedPrompt = ai.definePrompt({ + model: gemini15Flash, + name: 'augmentedPrompt', + input: { + schema: z.object({ + context: z.array(z.string()), + question: z.string(), + }), }, - ` + output: { + format: 'text', + }, + messages: ` Use the following context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer. {{#each context}} @@ -43,5 +41,5 @@ If you don't know the answer, just say that you don't know, don't try to make up Question: {{question}} Helpful Answer: -` -); +`, +}); diff --git a/scripts/release_main.sh b/scripts/release_main.sh index 1df0491da..9d41b080d 100755 --- a/scripts/release_main.sh +++ b/scripts/release_main.sh @@ -37,10 +37,6 @@ cd js/genkit pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT -cd js/plugins/dotprompt -pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com -cd $CURRENT - cd js/plugins/chroma pnpm publish --tag $RELEASE_TAG --publish-branch $RELEASE_BRANCH --registry https://wombat-dressing-room.appspot.com cd $CURRENT diff --git a/scripts/release_next.sh b/scripts/release_next.sh index c2374a5ff..ce49e0026 100755 --- a/scripts/release_next.sh +++ b/scripts/release_next.sh @@ -37,10 +37,6 @@ cd js/genkit pnpm publish --tag next --publish-branch next --registry https://wombat-dressing-room.appspot.com cd $CURRENT -cd js/plugins/dotprompt -pnpm publish --tag next --publish-branch next --registry https://wombat-dressing-room.appspot.com -cd $CURRENT - cd js/plugins/chroma pnpm publish --tag next --publish-branch next --registry https://wombat-dressing-room.appspot.com cd $CURRENT From d123a575eb1be8e18e4a6ab11f8ca7e676f26a2e Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Fri, 24 Jan 2025 21:37:36 -0500 Subject: [PATCH 266/562] breaking(js/genkit): moved chat/session to GenkitBeta APIs (#1659) --- js/doc-snippets/src/flows/index.ts | 3 +- js/doc-snippets/src/multi-agent/multi.ts | 2 +- js/doc-snippets/src/multi-agent/simple.ts | 3 +- js/genkit/package.json | 9 ++ js/genkit/src/beta.ts | 151 ++++++++++++++++++ js/genkit/src/genkit.ts | 118 -------------- js/genkit/tests/chat_test.ts | 6 +- js/genkit/tests/helpers.ts | 2 +- js/genkit/tests/prompts_test.ts | 24 +-- js/genkit/tests/session_test.ts | 4 +- .../google-cloud/tests/logs_session_test.ts | 5 +- js/plugins/google-cloud/tests/traces_test.ts | 5 +- 12 files changed, 189 insertions(+), 143 deletions(-) create mode 100644 js/genkit/src/beta.ts diff --git a/js/doc-snippets/src/flows/index.ts b/js/doc-snippets/src/flows/index.ts index a72bef3a8..938a82528 100644 --- a/js/doc-snippets/src/flows/index.ts +++ b/js/doc-snippets/src/flows/index.ts @@ -15,7 +15,8 @@ */ import { gemini15Flash, googleAI } from '@genkit-ai/googleai'; -import { genkit, z } from 'genkit'; +import { z } from 'genkit'; +import { genkit } from 'genkit/beta'; const ai = genkit({ plugins: [googleAI()], diff --git a/js/doc-snippets/src/multi-agent/multi.ts b/js/doc-snippets/src/multi-agent/multi.ts index b995ad803..dd259717b 100644 --- a/js/doc-snippets/src/multi-agent/multi.ts +++ b/js/doc-snippets/src/multi-agent/multi.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { genkit } from 'genkit'; +import { genkit } from 'genkit/beta'; const ai = genkit({}); const reservationTool = ai.defineTool( diff --git a/js/doc-snippets/src/multi-agent/simple.ts b/js/doc-snippets/src/multi-agent/simple.ts index f563f1c11..588b1b4f9 100644 --- a/js/doc-snippets/src/multi-agent/simple.ts +++ b/js/doc-snippets/src/multi-agent/simple.ts @@ -15,7 +15,8 @@ */ import { gemini15Pro } from '@genkit-ai/googleai'; -import { genkit, z } from 'genkit'; +import { z } from 'genkit'; +import { genkit } from 'genkit/beta'; const ai = genkit({}); diff --git a/js/genkit/package.json b/js/genkit/package.json index 620b7f528..6179fed8f 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -61,6 +61,12 @@ "import": "./lib/registry.mjs", "default": "./lib/registry.js" }, + "./beta": { + "types": "./lib/beta.d.ts", + "require": "./lib/beta.js", + "import": "./lib/beta.mjs", + "default": "./lib/beta.js" + }, "./tracing": { "types": "./lib/tracing.d.ts", "require": "./lib/tracing.js", @@ -157,6 +163,9 @@ "metrics": [ "lib/metrics" ], + "beta": [ + "lib/beta" + ], "registry": [ "lib/registry" ], diff --git a/js/genkit/src/beta.ts b/js/genkit/src/beta.ts new file mode 100644 index 000000000..266c8033b --- /dev/null +++ b/js/genkit/src/beta.ts @@ -0,0 +1,151 @@ +/** + * 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. + */ + +import { ExecutablePrompt, isExecutablePrompt } from '@genkit-ai/ai'; +import { Chat, ChatOptions } from '@genkit-ai/ai/chat'; +import { + Session, + SessionData, + SessionError, + SessionOptions, + getCurrentSession, +} from '@genkit-ai/ai/session'; +import { v4 as uuidv4 } from 'uuid'; +import { Genkit, GenkitOptions } from './genkit'; + +/** + * WARNING: these APIs are considered unstable and subject to frequent breaking changes that may not honor semver. + * + * Initializes Genkit BETA APIs with a set of options. + * + * This will create a new Genkit registry, register the provided plugins, stores, and other configuration. This + * should be called before any flows are registered. + */ +export function genkit(options: GenkitOptions): GenkitBeta { + return new GenkitBeta(options); +} + +/** + * Genkit BETA APIs. + */ +export class GenkitBeta extends Genkit { + /** + * Create a chat session with the provided options. + * + * ```ts + * const chat = ai.chat({ + * system: 'talk like a pirate', + * }) + * let response = await chat.send('tell me a joke') + * response = await chat.send('another one') + * ``` + */ + chat(options?: ChatOptions): Chat; + + /** + * Create a chat session with the provided preabmle. + * + * ```ts + * const triageAgent = ai.definePrompt({ + * system: 'help the user triage a problem', + * }) + * const chat = ai.chat(triageAgent) + * const { text } = await chat.send('my phone feels hot'); + * ``` + */ + chat(preamble: ExecutablePrompt, options?: ChatOptions): Chat; + + /** + * Create a chat session with the provided options. + * + * ```ts + * const chat = ai.chat({ + * system: 'talk like a pirate', + * }) + * let response = await chat.send('tell me a joke') + * response = await chat.send('another one') + * ``` + */ + chat( + preambleOrOptions?: ChatOptions | ExecutablePrompt, + maybeOptions?: ChatOptions + ): Chat { + let options: ChatOptions | undefined; + let preamble: ExecutablePrompt | undefined; + if (maybeOptions) { + options = maybeOptions; + } + if (preambleOrOptions) { + if (isExecutablePrompt(preambleOrOptions)) { + preamble = preambleOrOptions as ExecutablePrompt; + } else { + options = preambleOrOptions as ChatOptions; + } + } + + const session = this.createSession(); + if (preamble) { + return session.chat(preamble, options); + } + return session.chat(options); + } + + /** + * Create a session for this environment. + */ + createSession(options?: SessionOptions): Session { + const sessionId = uuidv4(); + const sessionData: SessionData = { + id: sessionId, + state: options?.initialState, + }; + return new Session(this.registry, { + id: sessionId, + sessionData, + store: options?.store, + }); + } + + /** + * Loads a session from the store. + */ + async loadSession( + sessionId: string, + options: SessionOptions + ): Promise { + if (!options.store) { + throw new Error('options.store is required'); + } + const sessionData = await options.store.get(sessionId); + + return new Session(this.registry, { + id: sessionId, + sessionData, + store: options.store, + }); + } + + /** + * Gets the current session from async local storage. + */ + currentSession(): Session { + const currentSession = getCurrentSession(this.registry); + if (!currentSession) { + throw new SessionError('not running within a session'); + } + return currentSession as Session; + } +} diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index 1f1f03b41..bef6ec7c2 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -52,13 +52,11 @@ import { evaluate, generate, generateStream, - isExecutablePrompt, loadPromptFolder, prompt, rerank, retrieve, } from '@genkit-ai/ai'; -import { Chat, ChatOptions } from '@genkit-ai/ai/chat'; import { EmbedderAction, EmbedderArgument, @@ -99,13 +97,6 @@ import { defineSimpleRetriever, index, } from '@genkit-ai/ai/retriever'; -import { - Session, - SessionData, - SessionError, - SessionOptions, - getCurrentSession, -} from '@genkit-ai/ai/session'; import { ToolFn } from '@genkit-ai/ai/tool'; import { Action, @@ -122,7 +113,6 @@ import { z, } from '@genkit-ai/core'; import { HasRegistry } from '@genkit-ai/core/registry'; -import { v4 as uuidv4 } from 'uuid'; import { BaseEvalDataPointSchema } from './evaluator.js'; import { logger } from './logging.js'; import { GenkitPlugin, genkitPlugin } from './plugin.js'; @@ -747,114 +737,6 @@ export class Genkit implements HasRegistry { } return generateStream(this.registry, options); } - - /** - * Create a chat session with the provided options. - * - * ```ts - * const chat = ai.chat({ - * system: 'talk like a pirate', - * }) - * let response = await chat.send('tell me a joke') - * response = await chat.send('another one') - * ``` - */ - chat(options?: ChatOptions): Chat; - - /** - * Create a chat session with the provided preabmle. - * - * ```ts - * const triageAgent = ai.definePrompt({ - * system: 'help the user triage a problem', - * }) - * const chat = ai.chat(triageAgent) - * const { text } = await chat.send('my phone feels hot'); - * ``` - */ - chat(preamble: ExecutablePrompt, options?: ChatOptions): Chat; - - /** - * Create a chat session with the provided options. - * - * ```ts - * const chat = ai.chat({ - * system: 'talk like a pirate', - * }) - * let response = await chat.send('tell me a joke') - * response = await chat.send('another one') - * ``` - */ - chat( - preambleOrOptions?: ChatOptions | ExecutablePrompt, - maybeOptions?: ChatOptions - ): Chat { - let options: ChatOptions | undefined; - let preamble: ExecutablePrompt | undefined; - if (maybeOptions) { - options = maybeOptions; - } - if (preambleOrOptions) { - if (isExecutablePrompt(preambleOrOptions)) { - preamble = preambleOrOptions as ExecutablePrompt; - } else { - options = preambleOrOptions as ChatOptions; - } - } - - const session = this.createSession(); - if (preamble) { - return session.chat(preamble, options); - } - return session.chat(options); - } - - /** - * Create a session for this environment. - */ - createSession(options?: SessionOptions): Session { - const sessionId = uuidv4(); - const sessionData: SessionData = { - id: sessionId, - state: options?.initialState, - }; - return new Session(this.registry, { - id: sessionId, - sessionData, - store: options?.store, - }); - } - - /** - * Loads a session from the store. - */ - async loadSession( - sessionId: string, - options: SessionOptions - ): Promise { - if (!options.store) { - throw new Error('options.store is required'); - } - const sessionData = await options.store.get(sessionId); - - return new Session(this.registry, { - id: sessionId, - sessionData, - store: options.store, - }); - } - - /** - * Gets the current session from async local storage. - */ - currentSession(): Session { - const currentSession = getCurrentSession(this.registry); - if (!currentSession) { - throw new SessionError('not running within a session'); - } - return currentSession as Session; - } - /** * A flow step that executes the provided function. Each run step is recorded separately in the trace. * diff --git a/js/genkit/tests/chat_test.ts b/js/genkit/tests/chat_test.ts index 2be245354..d83bf4f3b 100644 --- a/js/genkit/tests/chat_test.ts +++ b/js/genkit/tests/chat_test.ts @@ -18,7 +18,7 @@ import { MessageData } from '@genkit-ai/ai'; import { z } from '@genkit-ai/core'; import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; -import { Genkit, genkit } from '../src/genkit'; +import { GenkitBeta, genkit } from '../src/beta'; import { ProgrammableModel, defineEchoModel, @@ -26,7 +26,7 @@ import { } from './helpers'; describe('chat', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({ @@ -179,7 +179,7 @@ describe('chat', () => { }); describe('preamble', () => { - let ai: Genkit; + let ai: GenkitBeta; let pm: ProgrammableModel; beforeEach(() => { diff --git a/js/genkit/tests/helpers.ts b/js/genkit/tests/helpers.ts index f31662945..e5dcddcd1 100644 --- a/js/genkit/tests/helpers.ts +++ b/js/genkit/tests/helpers.ts @@ -17,6 +17,7 @@ import { MessageData } from '@genkit-ai/ai'; import { BaseEvalDataPoint } from '@genkit-ai/ai/evaluator'; import { ModelAction } from '@genkit-ai/ai/model'; +import { SessionData, SessionStore } from '@genkit-ai/ai/session'; import { StreamingCallback } from '@genkit-ai/core'; import { Genkit } from '../src/genkit'; import { @@ -24,7 +25,6 @@ import { GenerateResponseChunkData, GenerateResponseData, } from '../src/model'; -import { SessionData, SessionStore } from '../src/session'; export function defineEchoModel(ai: Genkit): ModelAction { const model = ai.defineModel( diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index aca2c4232..783182c35 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -17,7 +17,7 @@ import { ModelMiddleware, modelRef } from '@genkit-ai/ai/model'; import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; -import { Genkit, genkit } from '../src/genkit'; +import { GenkitBeta, genkit } from '../src/beta'; import { PromptAction, z } from '../src/index'; import { ProgrammableModel, @@ -62,7 +62,7 @@ const wrapResponse: ModelMiddleware = async (req, next) => { }; describe('definePrompt - functional', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({ @@ -174,7 +174,7 @@ describe('definePrompt - functional', () => { describe('definePrompt - dotprompt', () => { describe('default model', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({ @@ -310,7 +310,7 @@ describe('definePrompt - dotprompt', () => { }); describe('default model ref', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({ @@ -454,7 +454,7 @@ describe('definePrompt - dotprompt', () => { }); describe('explicit model', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({}); @@ -607,7 +607,7 @@ describe('definePrompt - dotprompt', () => { }); describe('render', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({}); @@ -637,7 +637,7 @@ describe('definePrompt - dotprompt', () => { describe('definePrompt', () => { describe('default model', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({ @@ -707,7 +707,7 @@ describe('definePrompt', () => { }); describe('default model ref', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({ @@ -767,7 +767,7 @@ describe('definePrompt', () => { }); describe('explicit model', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({}); @@ -849,7 +849,7 @@ describe('definePrompt', () => { }); describe('render', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({}); @@ -890,7 +890,7 @@ describe('definePrompt', () => { }); describe.only('prompt', () => { - let ai: Genkit; + let ai: GenkitBeta; let pm: ProgrammableModel; beforeEach(() => { @@ -1098,7 +1098,7 @@ describe.only('prompt', () => { }); describe('asTool', () => { - let ai: Genkit; + let ai: GenkitBeta; let pm: ProgrammableModel; beforeEach(() => { diff --git a/js/genkit/tests/session_test.ts b/js/genkit/tests/session_test.ts index 8e971747e..48db6f384 100644 --- a/js/genkit/tests/session_test.ts +++ b/js/genkit/tests/session_test.ts @@ -18,11 +18,11 @@ import { Message } from '@genkit-ai/ai'; import { SessionStore } from '@genkit-ai/ai/session'; import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; -import { Genkit, genkit } from '../src/genkit'; +import { GenkitBeta, genkit } from '../src/beta'; import { TestMemorySessionStore, defineEchoModel } from './helpers'; describe('session', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({ diff --git a/js/plugins/google-cloud/tests/logs_session_test.ts b/js/plugins/google-cloud/tests/logs_session_test.ts index 9113a3c96..08c8bb79d 100644 --- a/js/plugins/google-cloud/tests/logs_session_test.ts +++ b/js/plugins/google-cloud/tests/logs_session_test.ts @@ -25,7 +25,8 @@ import { } from '@jest/globals'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; -import { GenerateResponseData, Genkit, genkit, z } from 'genkit'; +import { GenerateResponseData, Genkit, z } from 'genkit'; +import { GenkitBeta, genkit } from 'genkit/beta'; import { ModelAction } from 'genkit/model'; import { Writable } from 'stream'; import { @@ -66,7 +67,7 @@ describe('GoogleCloudLogs for sessions', () => { next(); }; - let ai: Genkit; + let ai: GenkitBeta; let testModel: ModelAction; beforeAll(async () => { diff --git a/js/plugins/google-cloud/tests/traces_test.ts b/js/plugins/google-cloud/tests/traces_test.ts index 9ed08d53a..c80286a2a 100644 --- a/js/plugins/google-cloud/tests/traces_test.ts +++ b/js/plugins/google-cloud/tests/traces_test.ts @@ -25,7 +25,8 @@ import { } from '@jest/globals'; import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; -import { Genkit, genkit, z } from 'genkit'; +import { Genkit, z } from 'genkit'; +import { GenkitBeta, genkit } from 'genkit/beta'; import { appendSpan } from 'genkit/tracing'; import { __forceFlushSpansForTesting, @@ -56,7 +57,7 @@ jest.mock('../src/auth.js', () => { }); describe('GoogleCloudTracing', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeAll(async () => { process.env.GCLOUD_PROJECT = 'test'; From bf380291cc52db027c7c3e2ac7228ec6d1ff7e8f Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Sat, 25 Jan 2025 20:32:12 -0800 Subject: [PATCH 267/562] feat(js/ai): Adds GenerateOptions#resume, ToolAction#reply, Message#interrupts (#1660) --- js/ai/src/generate.ts | 78 ++++++++++++++ js/ai/src/generate/response.ts | 23 +++-- js/ai/src/index.ts | 1 + js/ai/src/message.ts | 8 ++ js/ai/src/tool.ts | 38 ++++++- js/ai/tests/generate/generate_test.ts | 143 +++++++++++++++++++++++++- js/ai/tests/tool_test.ts | 83 ++++++++++++++- js/core/src/error.ts | 1 + js/core/tests/flow_test.ts | 2 +- js/genkit/src/index.ts | 1 + 10 files changed, 359 insertions(+), 19 deletions(-) diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index 93e4f114c..f7a51c75a 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -42,6 +42,7 @@ import { ModelArgument, ModelMiddleware, Part, + ToolResponsePart, resolveModel, } from './model.js'; import { ExecutablePrompt } from './prompt.js'; @@ -59,6 +60,22 @@ export interface OutputOptions { jsonSchema?: any; } +/** ResumeOptions configure how to resume generation after an interrupt. */ +export interface ResumeOptions { + /** + * reply should contain a single or list of `toolResponse` parts corresponding + * to interrupt `toolRequest` parts from the most recent model message. Each + * entry must have a matching `name` and `ref` (if supplied) for its `toolRequest` + * counterpart. + * + * Tools have a `.reply` helper method to construct a reply ToolResponse and validate + * the data against its schema. Call `myTool.reply(interruptToolRequest, yourReplyData)`. + */ + reply: ToolResponsePart | ToolResponsePart[]; + /** Additional metadata to annotate the created tool message with in the "resume" key. */ + metadata?: Record; +} + export interface GenerateOptions< O extends z.ZodTypeAny = z.ZodTypeAny, CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, @@ -81,6 +98,27 @@ export interface GenerateOptions< config?: z.infer; /** Configuration for the desired output of the request. Defaults to the model's default output if unspecified. */ output?: OutputOptions; + /** + * resume provides convenient capabilities for continuing generation + * after an interrupt is triggered. Example: + * + * ```ts + * const myInterrupt = ai.defineInterrupt({...}); + * + * const response = await ai.generate({ + * tools: [myInterrupt], + * prompt: "Call myInterrupt", + * }); + * + * const interrupt = response.interrupts[0]; + * + * const resumedResponse = await ai.generate({ + * messages: response.messages, + * resume: myInterrupt.reply(interrupt, {note: "this is the reply data"}), + * }); + * ``` + */ + resume?: ResumeOptions; /** When true, return tool calls for manual processing instead of automatically resolving them. */ returnToolRequests?: boolean; /** Maximum number of tool call iterations that can be performed in a single generate call (default 5). */ @@ -97,6 +135,44 @@ export interface GenerateOptions< use?: ModelMiddleware[]; } +function applyResumeOption( + options: GenerateOptions, + messages: MessageData[] +): MessageData[] { + if (!options.resume) return []; + if ( + messages.at(-1)?.role !== 'model' || + !messages + .at(-1) + ?.content.find((p) => p.toolRequest && p.metadata?.interrupt) + ) { + throw new GenkitError({ + status: 'FAILED_PRECONDITION', + message: `Cannot 'resume' generation unless the previous message is a model message with at least one interrupt.`, + }); + } + const lastModelMessage = messages.at(-1)!; + const toolRequests = lastModelMessage.content.filter((p) => !!p.toolRequest); + + const pendingResponses: ToolResponsePart[] = toolRequests + .filter((t) => !!t.metadata?.pendingToolResponse) + .map((t) => ({ + toolResponse: t.metadata!.pendingToolResponse, + })) as ToolResponsePart[]; + + const reply = Array.isArray(options.resume.reply) + ? options.resume.reply + : [options.resume.reply]; + const message: MessageData = { + role: 'tool', + content: [...pendingResponses, ...reply], + metadata: { + resume: options.resume.metadata || true, + }, + }; + return [message]; +} + export async function toGenerateRequest( registry: Registry, options: GenerateOptions @@ -111,6 +187,8 @@ export async function toGenerateRequest( if (options.messages) { messages.push(...options.messages.map((m) => Message.parseData(m))); } + // resuming from interrupts occurs after message history but before user prompt + messages.push(...applyResumeOption(options, messages)); if (options.prompt) { messages.push({ role: 'user', diff --git a/js/ai/src/generate/response.ts b/js/ai/src/generate/response.ts index 0a31d5338..92ff2e1e6 100644 --- a/js/ai/src/generate/response.ts +++ b/js/ai/src/generate/response.ts @@ -115,11 +115,10 @@ export class GenerateResponse implements ModelResponseData { } /** - * If the selected candidate's message contains a `data` part, it is returned. Otherwise, + * If the generated message contains a `data` part, it is returned. Otherwise, * the `output()` method extracts the first valid JSON object or array from the text * contained in the selected candidate's message and returns it. * - * @param index The candidate index from which to extract output. If not provided, finds first candidate that conforms to output schema. * @returns The structured output contained in the selected candidate. */ get output(): O | null { @@ -127,8 +126,7 @@ export class GenerateResponse implements ModelResponseData { } /** - * Concatenates all `text` parts present in the candidate's message with no delimiter. - * @param index The candidate index from which to extract text, defaults to first candidate. + * Concatenates all `text` parts present in the generated message with no delimiter. * @returns A string of all concatenated text parts. */ get text(): string { @@ -136,9 +134,8 @@ export class GenerateResponse implements ModelResponseData { } /** - * Returns the first detected media part in the selected candidate's message. Useful for + * Returns the first detected media part in the generated message. Useful for * extracting (for example) an image from a generation expected to create one. - * @param index The candidate index from which to extract media, defaults to first candidate. * @returns The first detected `media` part in the candidate. */ get media(): { url: string; contentType?: string } | null { @@ -146,8 +143,7 @@ export class GenerateResponse implements ModelResponseData { } /** - * Returns the first detected `data` part of the selected candidate's message. - * @param index The candidate index from which to extract data, defaults to first candidate. + * Returns the first detected `data` part of the generated message. * @returns The first `data` part detected in the candidate (if any). */ get data(): O | null { @@ -155,14 +151,21 @@ export class GenerateResponse implements ModelResponseData { } /** - * Returns all tool request found in the candidate. - * @param index The candidate index from which to extract tool requests, defaults to first candidate. + * Returns all tool request found in the generated message. * @returns Array of all tool request found in the candidate. */ get toolRequests(): ToolRequestPart[] { return this.message?.toolRequests || []; } + /** + * Returns all tool requests annotated as interrupts found in the generated message. + * @returns A list of ToolRequestParts. + */ + get interrupts(): ToolRequestPart[] { + return this.message?.interrupts || []; + } + /** * Appends the message generated by the selected candidate to the messages already * present in the generation request. The result of this method can be safely diff --git a/js/ai/src/index.ts b/js/ai/src/index.ts index 2176b46ca..8d0cd6c37 100644 --- a/js/ai/src/index.ts +++ b/js/ai/src/index.ts @@ -47,6 +47,7 @@ export { type GenerateOptions, type GenerateStreamOptions, type GenerateStreamResponse, + type ResumeOptions, } from './generate.js'; export { Message } from './message.js'; export { diff --git a/js/ai/src/message.ts b/js/ai/src/message.ts index f93f0b151..a30274f95 100644 --- a/js/ai/src/message.ts +++ b/js/ai/src/message.ts @@ -122,6 +122,14 @@ export class Message implements MessageData { ) as ToolRequestPart[]; } + /** + * Returns all tool requests annotated with interrupt metadata. + * @returns Array of all interrupt tool requests. + */ + get interrupts(): ToolRequestPart[] { + return this.toolRequests.filter((t) => !!t.metadata?.interrupt); + } + /** * Converts the Message to a plain JS object. * @returns Plain JS object representing the data contained in the message. diff --git a/js/ai/src/tool.ts b/js/ai/src/tool.ts index 8ab428af8..fe2e7785c 100644 --- a/js/ai/src/tool.ts +++ b/js/ai/src/tool.ts @@ -14,11 +14,17 @@ * limitations under the License. */ -import { Action, defineAction, JSONSchema7, z } from '@genkit-ai/core'; +import { + Action, + defineAction, + JSONSchema7, + stripUndefinedProps, + z, +} from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; -import { toJsonSchema } from '@genkit-ai/core/schema'; +import { parseSchema, toJsonSchema } from '@genkit-ai/core/schema'; import { setCustomMetadataAttributes } from '@genkit-ai/core/tracing'; -import { ToolDefinition } from './model.js'; +import { ToolDefinition, ToolRequestPart, ToolResponsePart } from './model.js'; import { ExecutablePrompt } from './prompt.js'; /** @@ -33,6 +39,16 @@ export type ToolAction< type: 'tool'; }; }; + /** + * reply constructs a tool response corresponding to the provided interrupt tool request + * using the provided reply data, validating it against the output schema of the tool if + * it exists. + */ + reply( + interrupt: ToolRequestPart, + replyData: z.infer, + options?: { metadata?: Record } + ): ToolResponsePart; }; /** @@ -202,6 +218,22 @@ export function defineTool( interrupt: interruptTool, }) ); + (a as ToolAction).reply = (interrupt, replyData, options) => { + parseSchema(replyData, { + jsonSchema: config.outputJsonSchema, + schema: config.outputSchema, + }); + return { + toolResponse: stripUndefinedProps({ + name: interrupt.toolRequest.name, + ref: interrupt.toolRequest.ref, + output: replyData, + }), + metadata: { + reply: options?.metadata || true, + }, + }; + }; return a as ToolAction; } diff --git a/js/ai/tests/generate/generate_test.ts b/js/ai/tests/generate/generate_test.ts index 6b91ac249..872c40007 100644 --- a/js/ai/tests/generate/generate_test.ts +++ b/js/ai/tests/generate/generate_test.ts @@ -254,13 +254,148 @@ describe('toGenerateRequest', () => { output: {}, }, }, + { + should: + 'throw a PRECONDITION_FAILED error if trying to resume without a model message', + prompt: { + messages: [{ role: 'system', content: [{ text: 'sys' }] }], + resume: { + reply: { toolResponse: { name: 'test', output: { foo: 'bar' } } }, + }, + }, + throws: 'FAILED_PRECONDITION', + }, + { + should: + 'throw a PRECONDITION_FAILED error if trying to resume a model message without toolRequests', + prompt: { + messages: [ + { role: 'user', content: [{ text: 'hi' }] }, + { role: 'model', content: [{ text: 'there' }] }, + ], + resume: { + reply: { toolResponse: { name: 'test', output: { foo: 'bar' } } }, + }, + }, + throws: 'FAILED_PRECONDITION', + }, + { + should: 'add pending responses and interrupt replies to a tool message', + prompt: { + messages: [ + { role: 'user', content: [{ text: 'hey' }] }, + { + role: 'model', + content: [ + { + toolRequest: { name: 'p1', ref: '1', input: { one: '1' } }, + metadata: { + pendingToolResponse: { name: 'p1', ref: '1', output: 'done' }, + }, + }, + { + toolRequest: { name: 'p2', ref: '2', input: { one: '1' } }, + metadata: { + pendingToolResponse: { + name: 'p2', + ref: '2', + output: 'done2', + }, + }, + }, + { + toolRequest: { name: 'i1', ref: '3', input: { one: '1' } }, + metadata: { + interrupt: true, + }, + }, + { + toolRequest: { name: 'i2', ref: '4', input: { one: '1' } }, + metadata: { + interrupt: { sky: 'blue' }, + }, + }, + ], + }, + ], + resume: { + reply: [ + { toolResponse: { name: 'i1', ref: '3', output: 'done3' } }, + { toolResponse: { name: 'i2', ref: '4', output: 'done4' } }, + ], + }, + }, + expectedOutput: { + config: undefined, + docs: undefined, + output: {}, + tools: [], + messages: [ + { role: 'user', content: [{ text: 'hey' }] }, + { + role: 'model', + content: [ + { + toolRequest: { name: 'p1', ref: '1', input: { one: '1' } }, + metadata: { + pendingToolResponse: { name: 'p1', ref: '1', output: 'done' }, + }, + }, + { + toolRequest: { name: 'p2', ref: '2', input: { one: '1' } }, + metadata: { + pendingToolResponse: { + name: 'p2', + ref: '2', + output: 'done2', + }, + }, + }, + { + toolRequest: { name: 'i1', ref: '3', input: { one: '1' } }, + metadata: { + interrupt: true, + }, + }, + { + toolRequest: { name: 'i2', ref: '4', input: { one: '1' } }, + metadata: { + interrupt: { sky: 'blue' }, + }, + }, + ], + }, + { + role: 'tool', + metadata: { + resume: true, + }, + content: [ + { toolResponse: { name: 'p1', ref: '1', output: 'done' } }, + { toolResponse: { name: 'p2', ref: '2', output: 'done2' } }, + { toolResponse: { name: 'i1', ref: '3', output: 'done3' } }, + { toolResponse: { name: 'i2', ref: '4', output: 'done4' } }, + ], + }, + ], + }, + }, ]; for (const test of testCases) { it(test.should, async () => { - assert.deepStrictEqual( - await toGenerateRequest(registry, test.prompt as GenerateOptions), - test.expectedOutput - ); + if (test.throws) { + await assert.rejects( + async () => { + await toGenerateRequest(registry, test.prompt as GenerateOptions); + }, + { name: 'GenkitError', status: test.throws } + ); + } else { + assert.deepStrictEqual( + await toGenerateRequest(registry, test.prompt as GenerateOptions), + test.expectedOutput + ); + } }); } }); diff --git a/js/ai/tests/tool_test.ts b/js/ai/tests/tool_test.ts index 0fcf461d6..e1ac74098 100644 --- a/js/ai/tests/tool_test.ts +++ b/js/ai/tests/tool_test.ts @@ -18,7 +18,7 @@ import { z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; import * as assert from 'assert'; import { afterEach, describe, it } from 'node:test'; -import { defineInterrupt } from '../src/tool.js'; +import { defineInterrupt, defineTool } from '../src/tool.js'; describe('defineInterrupt', () => { let registry = new Registry(); @@ -103,3 +103,84 @@ describe('defineInterrupt', () => { }); }); }); + +describe('defineTool', () => { + let registry = new Registry(); + afterEach(() => { + registry = new Registry(); + }); + + describe('.reply()', () => { + it('constructs a ToolResponsePart', () => { + const t = defineTool( + registry, + { name: 'test', description: 'test' }, + async () => {} + ); + assert.deepStrictEqual( + t.reply({ toolRequest: { name: 'test', input: {} } }, 'output'), + { + toolResponse: { + name: 'test', + output: 'output', + }, + metadata: { + reply: true, + }, + } + ); + }); + + it('includes metadata', () => { + const t = defineTool( + registry, + { name: 'test', description: 'test' }, + async () => {} + ); + assert.deepStrictEqual( + t.reply({ toolRequest: { name: 'test', input: {} } }, 'output', { + metadata: { extra: 'data' }, + }), + { + toolResponse: { + name: 'test', + output: 'output', + }, + metadata: { + reply: { extra: 'data' }, + }, + } + ); + }); + + it('validates schema', () => { + const t = defineTool( + registry, + { name: 'test', description: 'test', outputSchema: z.number() }, + async (input, { interrupt }) => interrupt() + ); + assert.throws( + () => { + t.reply( + { toolRequest: { name: 'test', input: {} } }, + 'not_a_number' as any + ); + }, + { name: 'GenkitError', status: 'INVALID_ARGUMENT' } + ); + + assert.deepStrictEqual( + t.reply({ toolRequest: { name: 'test', input: {} } }, 55), + { + toolResponse: { + name: 'test', + output: 55, + }, + metadata: { + reply: true, + }, + } + ); + }); + }); +}); diff --git a/js/core/src/error.ts b/js/core/src/error.ts index 14056a9c1..b5fdaa574 100644 --- a/js/core/src/error.ts +++ b/js/core/src/error.ts @@ -38,6 +38,7 @@ export class GenkitError extends Error { super(`${source ? `${source}: ` : ''}${status}: ${message}`); this.status = status; this.detail = detail; + this.name = 'GenkitError'; } } diff --git a/js/core/tests/flow_test.ts b/js/core/tests/flow_test.ts index a4b0f76f0..aaeb64ef3 100644 --- a/js/core/tests/flow_test.ts +++ b/js/core/tests/flow_test.ts @@ -106,7 +106,7 @@ describe('flow', () => { async () => await testFlow({ foo: 'foo', bar: 'bar' } as any), (err: Error) => { return ( - err.name === 'Error' && + err.name === 'GenkitError' && err.message.includes('Schema validation failed') ); } diff --git a/js/genkit/src/index.ts b/js/genkit/src/index.ts index e19acb2e7..7dbb1f9cd 100644 --- a/js/genkit/src/index.ts +++ b/js/genkit/src/index.ts @@ -90,6 +90,7 @@ export { type RerankerInfo, type RerankerParams, type RerankerReference, + type ResumeOptions, type RetrieverAction, type RetrieverArgument, type RetrieverInfo, From 7cbac3c7ce1902c2fbd5e43d59df423076018ad6 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Sun, 26 Jan 2025 10:35:16 -0500 Subject: [PATCH 268/562] refactor(js/ai): drop namespace for dotprompt, allow async action registration, allow action names with slashes (#1662) --------- Co-authored-by: Michael Bleigh --- js/ai/src/generate.ts | 2 - js/ai/src/prompt.ts | 271 ++++++++++++++---------- js/ai/src/tool.ts | 3 +- js/core/src/action.ts | 78 ++++--- js/core/src/registry.ts | 39 +++- js/core/tests/registry_test.ts | 40 ++++ js/genkit/src/genkit.ts | 15 +- js/genkit/tests/prompts/sub/test.prompt | 5 + js/genkit/tests/prompts_test.ts | 40 +++- 9 files changed, 331 insertions(+), 162 deletions(-) create mode 100644 js/genkit/tests/prompts/sub/test.prompt diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index f7a51c75a..1081f9a07 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -424,8 +424,6 @@ async function resolveFullToolName( return `/tool/${name}`; } else if (await registry.lookupAction(`/prompt/${name}`)) { return `/prompt/${name}`; - } else if (await registry.lookupAction(`/prompt/dotprompt/${name}`)) { - return `/prompt/dotprompt/${name}`; } else { throw new Error(`Unable to determine type of of tool: ${name}`); } diff --git a/js/ai/src/prompt.ts b/js/ai/src/prompt.ts index dfb1ca79a..fb8bddd7c 100644 --- a/js/ai/src/prompt.ts +++ b/js/ai/src/prompt.ts @@ -16,7 +16,8 @@ import { Action, - defineAction, + ActionAsyncParams, + defineActionAsync, GenkitError, JSONSchema7, stripUndefinedProps, @@ -214,6 +215,22 @@ export function definePrompt< >( registry: Registry, options: PromptConfig +): ExecutablePrompt, O, CustomOptions> { + return definePromptAsync( + registry, + `${options.name}${options.variant ? `.${options.variant}` : ''}`, + Promise.resolve(options) + ); +} + +function definePromptAsync< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, +>( + registry: Registry, + name: string, + optionsPromise: Promise> ): ExecutablePrompt, O, CustomOptions> { const promptCache = {} as PromptCache; @@ -224,6 +241,7 @@ export function definePrompt< const messages: MessageData[] = []; renderOptions = { ...renderOptions }; // make a copy, we will be trimming const session = getCurrentSession(registry); + const resolvedOptions = await optionsPromise; // order of these matters: await renderSystemPrompt( @@ -231,7 +249,7 @@ export function definePrompt< session, input, messages, - options, + resolvedOptions, promptCache ); await renderMessages( @@ -239,7 +257,7 @@ export function definePrompt< session, input, messages, - options, + resolvedOptions, renderOptions, promptCache ); @@ -248,81 +266,93 @@ export function definePrompt< session, input, messages, - options, + resolvedOptions, promptCache ); let docs: DocumentData[] | undefined; - if (typeof options.docs === 'function') { - docs = await options.docs(input, { + if (typeof resolvedOptions.docs === 'function') { + docs = await resolvedOptions.docs(input, { state: session?.state, }); } else { - docs = options.docs; + docs = resolvedOptions.docs; } return stripUndefinedProps({ - model: options.model, - maxTurns: options.maxTurns, + model: resolvedOptions.model, + maxTurns: resolvedOptions.maxTurns, messages, docs, - tools: options.tools, - returnToolRequests: options.returnToolRequests, - toolChoice: options.toolChoice, - output: options.output, - use: options.use, + tools: resolvedOptions.tools, + returnToolRequests: resolvedOptions.returnToolRequests, + toolChoice: resolvedOptions.toolChoice, + output: resolvedOptions.output, + use: resolvedOptions.use, ...stripUndefinedProps(renderOptions), config: { - ...options?.config, + ...resolvedOptions?.config, ...renderOptions?.config, }, }); }; - const rendererAction = defineAction( - registry, - { - name: `${options.name}${options.variant ? `.${options.variant}` : ''}`, - inputJsonSchema: options.input?.jsonSchema, - inputSchema: options.input?.schema, - description: options.description, - actionType: 'prompt', - metadata: { - prompt: { - config: options.config, - input: { - schema: options.input ? toJsonSchema(options.input) : undefined, - }, - name: options.name, - model: modelName(options.model), + const rendererActionConfig = optionsPromise.then( + (options: PromptConfig) => { + const metadata = promptMetadata(options); + return { + name: `${options.name}${options.variant ? `.${options.variant}` : ''}`, + inputJsonSchema: options.input?.jsonSchema, + inputSchema: options.input?.schema, + description: options.description, + actionType: 'prompt', + metadata, + fn: async ( + input: z.infer + ): Promise> => { + return toGenerateRequest( + registry, + await renderOptionsFn(input, undefined) + ); }, - ...options.metadata, - type: 'prompt', - }, - }, - async (input: z.infer): Promise> => { - return toGenerateRequest( - registry, - await renderOptionsFn(input, undefined) - ); + } as ActionAsyncParams; } - ) as PromptAction; - const executablePromptAction = defineAction( + ); + const rendererAction = defineActionAsync( registry, - { - name: `${options.name}${options.variant ? `.${options.variant}` : ''}`, - inputJsonSchema: options.input?.jsonSchema, - inputSchema: options.input?.schema, - description: options.description, - actionType: 'executable-prompt', - metadata: { ...(options.metadata || { prompt: {} }), type: 'prompt' }, - }, - async (input: z.infer, { sendChunk }): Promise => { - return await generate(registry, { - ...(await renderOptionsFn(input, undefined)), - onChunk: sendChunk, - }); + 'prompt', + name, + rendererActionConfig + ) as Promise>; + + const executablePromptActionConfig = optionsPromise.then( + (options: PromptConfig) => { + const metadata = promptMetadata(options); + return { + name: `${options.name}${options.variant ? `.${options.variant}` : ''}`, + inputJsonSchema: options.input?.jsonSchema, + inputSchema: options.input?.schema, + description: options.description, + actionType: 'executable-prompt', + metadata, + fn: async ( + input: z.infer, + { sendChunk } + ): Promise => { + return await generate(registry, { + ...(await renderOptionsFn(input, undefined)), + onChunk: sendChunk, + }); + }, + } as ActionAsyncParams; } - ) as ExecutablePromptAction; + ); + + const executablePromptAction = defineActionAsync( + registry, + 'executable-prompt', + name, + executablePromptActionConfig + ) as Promise>; const executablePrompt = wrapInExecutablePrompt( registry, @@ -330,14 +360,35 @@ export function definePrompt< rendererAction ); - executablePromptAction.__executablePrompt = - executablePrompt as never as ExecutablePrompt>; - rendererAction.__executablePrompt = - executablePrompt as never as ExecutablePrompt>; + executablePromptAction.then((action) => { + action.__executablePrompt = executablePrompt as never as ExecutablePrompt< + z.infer + >; + }); + rendererAction.then((action) => { + action.__executablePrompt = executablePrompt as never as ExecutablePrompt< + z.infer + >; + }); return executablePrompt; } +function promptMetadata(options: PromptConfig) { + return { + ...options.metadata, + prompt: { + config: options.config, + input: { + schema: options.input ? toJsonSchema(options.input) : undefined, + }, + name: options.name, + model: modelName(options.model), + }, + type: 'prompt', + }; +} + function wrapInExecutablePrompt< I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, @@ -348,7 +399,7 @@ function wrapInExecutablePrompt< input: z.infer, renderOptions: PromptGenerateOptions | undefined ) => Promise, - rendererAction: PromptAction + rendererAction: Promise> ) { const executablePrompt = (async ( input?: I, @@ -376,7 +427,7 @@ function wrapInExecutablePrompt< }; executablePrompt.asTool = async (): Promise> => { - return rendererAction as unknown as ToolAction; + return (await rendererAction) as unknown as ToolAction; }; return executablePrompt; } @@ -560,11 +611,11 @@ export function isExecutablePrompt(obj: any): boolean { ); } -export async function loadPromptFolder( +export function loadPromptFolder( registry: Registry, dir: string = './prompts', ns: string -): Promise { +): void { const promptsPath = resolve(dir); if (existsSync(promptsPath)) { const dirEnts = readdirSync(promptsPath, { @@ -583,18 +634,17 @@ export async function loadPromptFolder( }) ); logger.debug( - `Registered Dotprompt partial "${partialName}" from "${join(dirEnt.path, dirEnt.name)}"` + `Registered Dotprompt partial "${partialName}" from "${join(dirEnt.parentPath, dirEnt.name)}"` ); } else { // If this prompt is in a subdirectory, we need to include that // in the namespace to prevent naming conflicts. let prefix = ''; - if (promptsPath !== dirEnt.path) { - prefix = dirEnt.path - .replace(`${promptsPath}/`, '') - .replace(/\//g, '-'); + let name = dirEnt.name; + if (promptsPath !== dirEnt.parentPath) { + prefix = dirEnt.parentPath.replace(`${promptsPath}/`, '') + '/'; } - await loadPrompt(registry, dirEnt.path, dirEnt.name, prefix, ns); + loadPrompt(registry, promptsPath, name, prefix, ns); } } } @@ -617,52 +667,57 @@ export function defineHelper( registry.dotprompt.defineHelper(name, fn); } -async function loadPrompt( +function loadPrompt( registry: Registry, path: string, filename: string, prefix = '', ns = 'dotprompt' -): Promise { - let name = `${prefix ? `${prefix}-` : ''}${basename(filename, '.prompt')}`; +): void { + let name = `${prefix ?? ''}${basename(filename, '.prompt')}`; let variant: string | null = null; if (name.includes('.')) { const parts = name.split('.'); name = parts[0]; variant = parts[1]; } - const source = readFileSync(join(path, filename), 'utf8'); + const source = readFileSync(join(path, (prefix ?? '') + filename), 'utf8'); const parsedPrompt = registry.dotprompt.parse(source); - const promptMetadata = await registry.dotprompt.renderMetadata(parsedPrompt); - if (variant) { - promptMetadata.variant = variant; - } - definePrompt(registry, { - name: registryDefinitionKey(name, variant ?? undefined, ns), - model: promptMetadata.model, - config: promptMetadata.config, - tools: promptMetadata.tools, - description: promptMetadata.description, - output: { - jsonSchema: promptMetadata.output?.schema, - format: promptMetadata.output?.format, - }, - input: { - jsonSchema: promptMetadata.input?.schema, - }, - metadata: { - ...promptMetadata.metadata, - type: 'prompt', - prompt: { - ...promptMetadata, - template: source, - }, - }, - maxTurns: promptMetadata.raw?.['maxTurns'], - toolChoice: promptMetadata.raw?.['toolChoice'], - returnToolRequests: promptMetadata.raw?.['returnToolRequests'], - messages: parsedPrompt.template, - }); + definePromptAsync( + registry, + registryDefinitionKey(name, variant ?? undefined, ns), + registry.dotprompt.renderMetadata(parsedPrompt).then((promptMetadata) => { + if (variant) { + promptMetadata.variant = variant; + } + return { + name: registryDefinitionKey(name, variant ?? undefined, ns), + model: promptMetadata.model, + config: promptMetadata.config, + tools: promptMetadata.tools, + description: promptMetadata.description, + output: { + jsonSchema: promptMetadata.output?.schema, + format: promptMetadata.output?.format, + }, + input: { + jsonSchema: promptMetadata.input?.schema, + }, + metadata: { + ...promptMetadata.metadata, + type: 'prompt', + prompt: { + ...promptMetadata, + template: source, + }, + }, + maxTurns: promptMetadata.raw?.['maxTurns'], + toolChoice: promptMetadata.raw?.['toolChoice'], + returnToolRequests: promptMetadata.raw?.['returnToolRequests'], + messages: parsedPrompt.template, + }; + }) + ); } export async function prompt< @@ -694,11 +749,9 @@ async function lookupPrompt< name: string, variant?: string ): Promise> { - let registryPrompt = - (await registry.lookupAction(registryLookupKey(name, variant))) || - (await registry.lookupAction( - registryLookupKey(name, variant, 'dotprompt') - )); + let registryPrompt = await registry.lookupAction( + registryLookupKey(name, variant) + ); if (registryPrompt) { return (registryPrompt as PromptAction) .__executablePrompt as never as ExecutablePrompt; diff --git a/js/ai/src/tool.ts b/js/ai/src/tool.ts index fe2e7785c..d0d416ecd 100644 --- a/js/ai/src/tool.ts +++ b/js/ai/src/tool.ts @@ -143,8 +143,7 @@ export async function lookupToolByName( let tool = (await registry.lookupAction(name)) || (await registry.lookupAction(`/tool/${name}`)) || - (await registry.lookupAction(`/prompt/${name}`)) || - (await registry.lookupAction(`/prompt/dotprompt/${name}`)); + (await registry.lookupAction(`/prompt/${name}`)); if (!tool) { throw new Error(`Tool ${name} not found`); } diff --git a/js/core/src/action.ts b/js/core/src/action.ts index a70f8eee6..116ef9ebf 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -155,6 +155,17 @@ export type ActionParams< actionType: ActionType; }; +export type ActionAsyncParams< + I extends z.ZodTypeAny, + O extends z.ZodTypeAny, + S extends z.ZodTypeAny = z.ZodTypeAny, +> = ActionParams & { + fn: ( + input: z.infer, + options: ActionFnArg> + ) => Promise>; +}; + export type SimpleMiddleware = ( req: I, next: (req?: I) => Promise @@ -377,30 +388,6 @@ export function action< return actionFn; } -function validateActionName(registry: Registry, name: string) { - if (name.includes('/')) { - validatePluginName(registry, name.split('/', 1)[0]); - validateActionId(name.substring(name.indexOf('/') + 1)); - } - return name; -} - -function validatePluginName(registry: Registry, pluginId: string) { - if (!registry.lookupPlugin(pluginId)) { - throw new Error( - `Unable to find plugin name used in the action name: ${pluginId}` - ); - } - return pluginId; -} - -function validateActionId(actionId: string) { - if (actionId.includes('/')) { - throw new Error(`Action name must not include slashes (/): ${actionId}`); - } - return actionId; -} - /** * Defines an action with the given config and registers it in the registry. */ @@ -422,11 +409,6 @@ export function defineAction< 'See: https://github.com/firebase/genkit/blob/main/docs/errors/no_new_actions_at_runtime.md' ); } - if (typeof config.name === 'string') { - validateActionName(registry, config.name); - } else { - validateActionId(config.name.actionId); - } const act = action( registry, config, @@ -440,6 +422,44 @@ export function defineAction< return act; } +/** + * Defines an action with the given config promise and registers it in the registry. + */ +export function defineActionAsync< + I extends z.ZodTypeAny, + O extends z.ZodTypeAny, + S extends z.ZodTypeAny = z.ZodTypeAny, +>( + registry: Registry, + actionType: ActionType, + name: + | string + | { + pluginId: string; + actionId: string; + }, + config: Promise> +): Promise> { + const actionName = + typeof name === 'string' ? name : `${name.pluginId}/${name.actionId}`; + const actionPromise = config.then((resolvedConfig) => { + const act = action( + registry, + resolvedConfig, + async (i: I, options): Promise> => { + await registry.initializeAllPlugins(); + return await runInActionRuntimeContext(registry, () => + resolvedConfig.fn(i, options) + ); + } + ); + act.__action.actionType = actionType; + return act; + }); + registry.registerActionAsync(actionType, actionName, actionPromise); + return actionPromise; +} + // Streaming callback function. export type StreamingCallback = (chunk: T) => void; diff --git a/js/core/src/registry.ts b/js/core/src/registry.ts index bdf5462d1..74481fe61 100644 --- a/js/core/src/registry.ts +++ b/js/core/src/registry.ts @@ -52,7 +52,7 @@ export interface Schema { function parsePluginName(registryKey: string) { const tokens = registryKey.split('/'); - if (tokens.length === 4) { + if (tokens.length >= 4) { return tokens[2]; } return undefined; @@ -64,7 +64,11 @@ type ActionsRecord = Record>; * The registry is used to store and lookup actions, trace stores, flow state stores, plugins, and schemas. */ export class Registry { - private actionsById: Record> = {}; + private actionsById: Record< + string, + | Action + | Promise> + > = {}; private pluginsByName: Record = {}; private schemasByName: Record = {}; private valueByTypeAndName: Record> = {}; @@ -110,7 +114,9 @@ export class Registry { if (!this.actionsById[key] && pluginName) { await this.initializePlugin(pluginName); } - return (this.actionsById[key] as R) || this.parent?.lookupAction(key); + return ( + ((await this.actionsById[key]) as R) || this.parent?.lookupAction(key) + ); } /** @@ -133,15 +139,40 @@ export class Registry { this.actionsById[key] = action; } + /** + * Registers an action promise in the registry. + */ + registerActionAsync( + type: ActionType, + name: string, + action: Promise> + ) { + const key = `/${type}/${name}`; + logger.debug(`registering ${key} (async)`); + if (this.actionsById.hasOwnProperty(key)) { + // TODO: Make this an error! + logger.warn( + `WARNING: ${key} already has an entry in the registry. Overwriting.` + ); + } + this.actionsById[key] = action; + } + /** * Returns all actions in the registry. * @returns All actions in the registry. */ async listActions(): Promise { await this.initializeAllPlugins(); + const actions: Record> = {}; + await Promise.all( + Object.entries(this.actionsById).map(async ([key, action]) => { + actions[key] = await action; + }) + ); return { ...(await this.parent?.listActions()), - ...this.actionsById, + ...actions, }; } diff --git a/js/core/tests/registry_test.ts b/js/core/tests/registry_test.ts index 86d7a2861..357620ca6 100644 --- a/js/core/tests/registry_test.ts +++ b/js/core/tests/registry_test.ts @@ -69,6 +69,7 @@ describe('registry class', () => { name: 'bar', async initializer() { registry.registerAction('model', barSomethingAction); + registry.registerAction('model', barSubSomethingAction); return {}; }, }); @@ -83,10 +84,22 @@ describe('registry class', () => { }, async () => null ); + const barSubSomethingAction = action( + registry, + { + name: { + pluginId: 'bar', + actionId: 'sub/something', + }, + actionType: 'util', + }, + async () => null + ); assert.deepEqual(await registry.listActions(), { '/model/foo/something': fooSomethingAction, '/model/bar/something': barSomethingAction, + '/model/bar/sub/something': barSubSomethingAction, }); }); @@ -159,6 +172,12 @@ describe('registry class', () => { async () => null ); registry.registerAction('model', barSomethingAction); + const barSubSomethingAction = action( + registry, + { name: 'sub/bar_something', actionType: 'util' }, + async () => null + ); + registry.registerAction('model', barSubSomethingAction); assert.strictEqual( await registry.lookupAction('/model/foo_something'), @@ -168,6 +187,10 @@ describe('registry class', () => { await registry.lookupAction('/model/bar_something'), barSomethingAction ); + assert.strictEqual( + await registry.lookupAction('/model/sub/bar_something'), + barSubSomethingAction + ); }); it('returns action registered by plugin', async () => { @@ -175,6 +198,7 @@ describe('registry class', () => { name: 'foo', async initializer() { registry.registerAction('model', somethingAction); + registry.registerAction('model', subSomethingAction); return {}; }, }); @@ -189,11 +213,27 @@ describe('registry class', () => { }, async () => null ); + const subSomethingAction = action( + registry, + { + name: { + pluginId: 'foo', + actionId: 'sub/something', + }, + actionType: 'util', + }, + async () => null + ); assert.strictEqual( await registry.lookupAction('/model/foo/something'), somethingAction ); + + assert.strictEqual( + await registry.lookupAction('/model/foo/sub/something'), + subSomethingAction + ); }); it('returns undefined for unknown action', async () => { diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index bef6ec7c2..0e2435a27 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -115,7 +115,7 @@ import { import { HasRegistry } from '@genkit-ai/core/registry'; import { BaseEvalDataPointSchema } from './evaluator.js'; import { logger } from './logging.js'; -import { GenkitPlugin, genkitPlugin } from './plugin.js'; +import { GenkitPlugin } from './plugin.js'; import { Registry } from './registry.js'; /** @@ -801,14 +801,11 @@ export class Genkit implements HasRegistry { ); } if (this.options.promptDir !== null) { - const dotprompt = genkitPlugin('dotprompt', async (ai) => { - await loadPromptFolder( - this.registry, - this.options.promptDir ?? './prompts', - 'dotprompt' - ); - }); - plugins.push(dotprompt); + loadPromptFolder( + this.registry, + this.options.promptDir ?? './prompts', + '' + ); } plugins.forEach((plugin) => { const loadedPlugin = plugin(this); diff --git a/js/genkit/tests/prompts/sub/test.prompt b/js/genkit/tests/prompts/sub/test.prompt new file mode 100644 index 000000000..42237b4b7 --- /dev/null +++ b/js/genkit/tests/prompts/sub/test.prompt @@ -0,0 +1,5 @@ +--- +config: + temperature: 12 +--- +Hello from the sub folder prompt file \ No newline at end of file diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index 783182c35..13622cda6 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -911,9 +911,41 @@ describe.only('prompt', () => { text, 'Echo: Hello from the prompt file; config: {"temperature":11}' ); + assert.deepStrictEqual(await testPrompt.render({}), { + config: { + temperature: 11, + }, + messages: [ + { content: [{ text: 'Hello from the prompt file' }], role: 'user' }, + ], + output: {}, + }); + }); + + it.only('loads from from the sub folder', async () => { + const testPrompt = ai.prompt('sub/test'); // see tests/prompts/sub folder + + const { text } = await testPrompt(); + + assert.strictEqual( + text, + 'Echo: Hello from the sub folder prompt file; config: {"temperature":12}' + ); + assert.deepStrictEqual(await testPrompt.render({}), { + config: { + temperature: 12, + }, + messages: [ + { + content: [{ text: 'Hello from the sub folder prompt file' }], + role: 'user', + }, + ], + output: {}, + }); }); - it.only('loads from from the folder with all the options', async () => { + it('loads from from the folder with all the options', async () => { const testPrompt = ai.prompt('kitchensink'); // see tests/prompts folder const request = await testPrompt.render({ subject: 'banana' }); @@ -1164,9 +1196,6 @@ describe('asTool', () => { outputSchema: { $schema: 'http://json-schema.org/draft-07/schema#', }, - metadata: { - originalName: 'dotprompt/toolPrompt', - }, }, ], }); @@ -1376,9 +1405,6 @@ describe('asTool', () => { outputSchema: { $schema: 'http://json-schema.org/draft-07/schema#', }, - metadata: { - originalName: 'dotprompt/toolPrompt', - }, }, ], }); From 9ff158ea2c2edfa5046d00166c51e1b77503d580 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Sun, 26 Jan 2025 10:27:53 -0800 Subject: [PATCH 269/562] chore(js): Bump dotprompt for breaking classname change. (#1665) --- js/ai/package.json | 2 +- js/core/package.json | 2 +- js/core/src/registry.ts | 4 ++-- js/plugins/evaluators/package.json | 2 +- js/plugins/evaluators/src/metrics/helper.ts | 4 ++-- js/pnpm-lock.yaml | 18 +++++++++--------- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/js/ai/package.json b/js/ai/package.json index 757db31df..dd1c1d953 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -35,7 +35,7 @@ "node-fetch": "^3.3.2", "partial-json": "^0.1.7", "uuid": "^10.0.0", - "dotprompt": "^1.0.0-dev || ^1" + "dotprompt": "^1.0.0-dev.3 || ^1" }, "devDependencies": { "npm-run-all": "^4.1.5", diff --git a/js/core/package.json b/js/core/package.json index ce78aebfd..ad6a9b791 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -43,7 +43,7 @@ "json-schema": "^0.4.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.22.4", - "dotprompt": "^1.0.0-dev || ^1" + "dotprompt": "^1.0.0-dev.3 || ^1" }, "devDependencies": { "@types/express": "^4.17.21", diff --git a/js/core/src/registry.ts b/js/core/src/registry.ts index 74481fe61..1768cb0cf 100644 --- a/js/core/src/registry.ts +++ b/js/core/src/registry.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { DotpromptEnvironment } from 'dotprompt'; +import { Dotprompt } from 'dotprompt'; import { AsyncLocalStorage } from 'node:async_hooks'; import * as z from 'zod'; import { Action } from './action.js'; @@ -75,7 +75,7 @@ export class Registry { private allPluginsInitialized = false; readonly asyncStore = new AsyncStore(); - readonly dotprompt = new DotpromptEnvironment({ + readonly dotprompt = new Dotprompt({ schemaResolver: async (name) => { const resolvedSchema = await this.lookupSchema(name); if (!resolvedSchema) { diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 764f99b1f..6c50d3d73 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -33,7 +33,7 @@ "compute-cosine-similarity": "^1.1.0", "node-fetch": "^3.3.2", "path": "^0.12.7", - "dotprompt": "^1.0.0-dev || ^1" + "dotprompt": "^1.0.0-dev.3 || ^1" }, "peerDependencies": { "genkit": "workspace:*" diff --git a/js/plugins/evaluators/src/metrics/helper.ts b/js/plugins/evaluators/src/metrics/helper.ts index ec59c2fc4..e5a4bcccc 100644 --- a/js/plugins/evaluators/src/metrics/helper.ts +++ b/js/plugins/evaluators/src/metrics/helper.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { dotprompt, PromptFunction } from 'dotprompt'; +import { Dotprompt, PromptFunction } from 'dotprompt'; import { readFileSync } from 'fs'; /** Helper function to get current directory, isolated in a separate file to work with ESM */ @@ -22,7 +22,7 @@ export function getDirName() { return __dirname; } -const dp = dotprompt(); +const dp = new Dotprompt(); export function loadPromptFile(path: string): Promise { return dp.compile(dp.parse(readFileSync(path, 'utf8'))); diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 60faabe3b..1a13025d3 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -51,8 +51,8 @@ importers: specifier: ^2.0.20 version: 2.0.20 dotprompt: - specifier: ^1.0.0-dev || ^1 - version: 1.0.0-dev.2 + specifier: ^1.0.0-dev.3 || ^1 + version: 1.0.0-dev.3 json5: specifier: ^2.2.3 version: 2.2.3 @@ -121,8 +121,8 @@ importers: specifier: ^2.8.5 version: 2.8.5 dotprompt: - specifier: ^1.0.0-dev || ^1 - version: 1.0.0-dev.2 + specifier: ^1.0.0-dev.3 || ^1 + version: 1.0.0-dev.3 express: specifier: ^4.21.0 version: 4.21.0 @@ -337,8 +337,8 @@ importers: specifier: ^1.1.0 version: 1.1.0 dotprompt: - specifier: ^1.0.0-dev || ^1 - version: 1.0.0-dev.2 + specifier: ^1.0.0-dev.3 || ^1 + version: 1.0.0-dev.3 genkit: specifier: workspace:* version: link:../../genkit @@ -4209,8 +4209,8 @@ packages: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} - dotprompt@1.0.0-dev.2: - resolution: {integrity: sha512-RcpdKpKd1XemHpb6Lti/ekLGs+9or+11n/aUSZ9e8QM1qTNFUloa9bdqg4OJDvpAlihv34uCt8yUlV2h4MglqA==} + dotprompt@1.0.0-dev.3: + resolution: {integrity: sha512-gAcIhG+vzZlrZcBN/lO8gEMUtXHKMLbs1+snJuZrPkZnNgnWrzz1hYN4vYotpng/5fHsYSPUEckECy6qUNSrsQ==} duplexify@4.1.3: resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} @@ -9514,7 +9514,7 @@ snapshots: dotenv@16.4.5: {} - dotprompt@1.0.0-dev.2: + dotprompt@1.0.0-dev.3: dependencies: handlebars: 4.7.8 yaml: 2.6.1 From 7c65ececcdf571fe28ec7ec37995dd4a6274894b Mon Sep 17 00:00:00 2001 From: Michael Doyle Date: Sun, 26 Jan 2025 22:45:46 -0500 Subject: [PATCH 270/562] fix(js/plugins/google-cloud): Catch errors looking for ADC (#1664) We have some helpful error messaging to help users apply the necessary roles to their GCP service accounts for telemetry exports. We look up the logged in service account to customize the messaging. If the lookup fails we need to handle those errors properly. --- js/plugins/google-cloud/src/auth.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/js/plugins/google-cloud/src/auth.ts b/js/plugins/google-cloud/src/auth.ts index 04b3a07d9..61aced229 100644 --- a/js/plugins/google-cloud/src/auth.ts +++ b/js/plugins/google-cloud/src/auth.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { logger } from 'genkit/logging'; -import { auth, GoogleAuth } from 'google-auth-library'; +import { auth, CredentialBody, GoogleAuth } from 'google-auth-library'; import { GcpPrincipal, GcpTelemetryConfig } from './types.js'; /** @@ -38,6 +38,7 @@ export async function credentialsFromEnvironment(): Promise< let options: Partial = {}; if (process.env.GCLOUD_SERVICE_ACCOUNT_CREDS) { + logger.debug('Retrieving credentials from GCLOUD_SERVICE_ACCOUNT_CREDS'); const serviceAccountCreds = JSON.parse( process.env.GCLOUD_SERVICE_ACCOUNT_CREDS ); @@ -69,7 +70,12 @@ export async function credentialsFromEnvironment(): Promise< **/ export async function resolveCurrentPrincipal(): Promise { const envCredentials = await credentialsFromEnvironment(); - const adcCredentials = await auth.getCredentials(); + let adcCredentials = {} as CredentialBody; + try { + adcCredentials = await auth.getCredentials(); + } catch (e) { + logger.debug('Could not retrieve client_email from ADC.'); + } // TODO(michaeldoyle): How to look up if the user provided credentials in the // plugin config (i.e. GcpTelemetryOptions) From 21ffd9b088c54eefa7fb5753cf4cda172ff628e9 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 27 Jan 2025 08:18:17 -0500 Subject: [PATCH 271/562] feat(js): propagate context to sub actions, expose context in prompts (#1663) --- docs/auth.md | 6 +- js/ai/src/generate.ts | 22 +- js/ai/src/prompt.ts | 66 ++++- js/ai/src/tool.ts | 8 +- js/ai/tests/prompt/prompt_test.ts | 300 +++++++++++++++++++- js/core/src/action.ts | 24 +- js/core/src/context.ts | 42 ++- js/core/src/flow.ts | 8 +- js/core/src/index.ts | 2 +- js/core/tests/action_test.ts | 23 ++ js/core/tests/flow_test.ts | 16 +- js/genkit/src/genkit.ts | 11 + js/genkit/src/index.ts | 2 +- js/genkit/tests/generate_test.ts | 59 ++++ js/plugins/express/src/index.ts | 8 +- js/plugins/express/tests/express_test.ts | 4 +- js/plugins/firebase/tests/functions_test.ts | 4 +- js/testapps/express/src/index.ts | 2 +- 18 files changed, 521 insertions(+), 86 deletions(-) diff --git a/docs/auth.md b/docs/auth.md index 000c34785..aedeb4f8d 100644 --- a/docs/auth.md +++ b/docs/auth.md @@ -73,7 +73,7 @@ When running with the Genkit Development UI, you can pass the Auth object by entering JSON in the "Auth JSON" tab: `{"uid": "abc-def"}`. You can also retrieve the auth context for the flow at any time within the flow -by calling `getFlowAuth()`, including in functions invoked by the flow: +by calling `ai.currentContext()`, including in functions invoked by the flow: ```ts import { genkit, z } from 'genkit'; @@ -81,7 +81,7 @@ import { genkit, z } from 'genkit'; const ai = genkit({ ... });; async function readDatabase(uid: string) { - const auth = ai.getAuthContext(); + const auth = ai.currentContext()?.auth; if (auth?.admin) { // Do something special if the user is an admin } else { @@ -153,7 +153,7 @@ export const selfSummaryFlow = onFlow( When using the Firebase Auth plugin, `user` will be returned as a [DecodedIdToken](https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.decodedidtoken). -You can always retrieve this object at any time via `getFlowAuth()` as noted +You can always retrieve this object at any time via `ai.currentContext()` as noted above. When running this flow during development, you would pass the user object in the same way: diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index 1081f9a07..8579142c3 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -16,8 +16,10 @@ import { Action, + ActionContext, GenkitError, StreamingCallback, + runWithContext, runWithStreamingCallback, sentinelNoopStreamingCallback, z, @@ -113,8 +115,8 @@ export interface GenerateOptions< * const interrupt = response.interrupts[0]; * * const resumedResponse = await ai.generate({ - * messages: response.messages, - * resume: myInterrupt.reply(interrupt, {note: "this is the reply data"}), + * messages: response.messages, + * resume: myInterrupt.reply(interrupt, {note: "this is the reply data"}), * }); * ``` */ @@ -133,6 +135,8 @@ export interface GenerateOptions< streamingCallback?: StreamingCallback; /** Middleware to be used with this model call. */ use?: ModelMiddleware[]; + /** Additional context (data, like e.g. auth) to be passed down to tools, prompts and other sub actions. */ + context?: ActionContext; } function applyResumeOption( @@ -376,10 +380,16 @@ export async function generate< registry, stripNoop(resolvedOptions.onChunk ?? resolvedOptions.streamingCallback), async () => { - const response = await generateHelper(registry, { - rawRequest: params, - middleware: resolvedOptions.use, - }); + const generateFn = () => + generateHelper(registry, { + rawRequest: params, + middleware: resolvedOptions.use, + }); + const response = await runWithContext( + registry, + resolvedOptions.context, + generateFn + ); const request = await toGenerateRequest(registry, { ...resolvedOptions, tools, diff --git a/js/ai/src/prompt.ts b/js/ai/src/prompt.ts index fb8bddd7c..abfe82e6d 100644 --- a/js/ai/src/prompt.ts +++ b/js/ai/src/prompt.ts @@ -17,8 +17,10 @@ import { Action, ActionAsyncParams, + ActionContext, defineActionAsync, GenkitError, + getContext, JSONSchema7, stripUndefinedProps, z, @@ -117,6 +119,7 @@ export interface PromptConfig< tools?: ToolArgument[]; toolChoice?: ToolChoice; use?: ModelMiddleware[]; + context?: ActionContext; } /** @@ -179,6 +182,7 @@ export type PartsResolver = ( input: I, options: { state?: S; + context: ActionContext; } ) => Part[] | Promise; @@ -187,12 +191,14 @@ export type MessagesResolver = ( options: { history?: MessageData[]; state?: S; + context: ActionContext; } ) => MessageData[] | Promise; export type DocsResolver = ( input: I, options: { + context: ActionContext; state?: S; } ) => DocumentData[] | Promise; @@ -250,7 +256,8 @@ function definePromptAsync< input, messages, resolvedOptions, - promptCache + promptCache, + renderOptions ); await renderMessages( registry, @@ -267,13 +274,15 @@ function definePromptAsync< input, messages, resolvedOptions, - promptCache + promptCache, + renderOptions ); let docs: DocumentData[] | undefined; if (typeof resolvedOptions.docs === 'function') { docs = await resolvedOptions.docs(input, { state: session?.state, + context: renderOptions?.context || getContext(registry) || {}, }); } else { docs = resolvedOptions.docs; @@ -287,6 +296,7 @@ function definePromptAsync< tools: resolvedOptions.tools, returnToolRequests: resolvedOptions.returnToolRequests, toolChoice: resolvedOptions.toolChoice, + context: resolvedOptions.context, output: resolvedOptions.output, use: resolvedOptions.use, ...stripUndefinedProps(renderOptions), @@ -442,13 +452,17 @@ async function renderSystemPrompt< input: z.infer, messages: MessageData[], options: PromptConfig, - promptCache: PromptCache + promptCache: PromptCache, + renderOptions: PromptGenerateOptions | undefined ) { if (typeof options.system === 'function') { messages.push({ role: 'system', content: normalizeParts( - await options.system(input, { state: session?.state }) + await options.system(input, { + state: session?.state, + context: renderOptions?.context || getContext(registry) || {}, + }) ), }); } else if (typeof options.system === 'string') { @@ -458,7 +472,14 @@ async function renderSystemPrompt< } messages.push({ role: 'system', - content: await renderDotpromptToParts(promptCache.system, input, session), + content: await renderDotpromptToParts( + registry, + promptCache.system, + input, + session, + options, + renderOptions + ), }); } else if (options.system) { messages.push({ @@ -486,6 +507,7 @@ async function renderMessages< messages.push( ...(await options.messages(input, { state: session?.state, + context: renderOptions?.context || getContext(registry) || {}, history: renderOptions?.messages, })) ); @@ -498,7 +520,10 @@ async function renderMessages< } const rendered = await promptCache.messages({ input, - context: { state: session?.state }, + context: { + ...(renderOptions?.context || getContext(registry)), + state: session?.state, + }, messages: renderOptions?.messages?.map((m) => Message.parseData(m) ) as DpMessage[], @@ -528,13 +553,17 @@ async function renderUserPrompt< input: z.infer, messages: MessageData[], options: PromptConfig, - promptCache: PromptCache + promptCache: PromptCache, + renderOptions: PromptGenerateOptions | undefined ) { if (typeof options.prompt === 'function') { messages.push({ role: 'user', content: normalizeParts( - await options.prompt(input, { state: session?.state }) + await options.prompt(input, { + state: session?.state, + context: renderOptions?.context || getContext(registry) || {}, + }) ), }); } else if (typeof options.prompt === 'string') { @@ -545,9 +574,12 @@ async function renderUserPrompt< messages.push({ role: 'user', content: await renderDotpromptToParts( + registry, promptCache.userPrompt, input, - session + session, + options, + renderOptions ), }); } else if (options.prompt) { @@ -585,14 +617,24 @@ function normalizeParts(parts: string | Part | Part[]): Part[] { return [parts as Part]; } -async function renderDotpromptToParts( +async function renderDotpromptToParts< + I extends z.ZodTypeAny = z.ZodTypeAny, + O extends z.ZodTypeAny = z.ZodTypeAny, + CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, +>( + registry: Registry, promptFn: PromptFunction, input: any, - session?: Session + session: Session | undefined, + options: PromptConfig, + renderOptions: PromptGenerateOptions | undefined ): Promise { const renderred = await promptFn({ input, - context: { state: session?.state }, + context: { + ...(renderOptions?.context || getContext(registry)), + state: session?.state, + }, }); if (renderred.messages.length !== 1) { throw new Error('parts tempate must produce only one message'); diff --git a/js/ai/src/tool.ts b/js/ai/src/tool.ts index d0d416ecd..da9133560 100644 --- a/js/ai/src/tool.ts +++ b/js/ai/src/tool.ts @@ -16,6 +16,7 @@ import { Action, + ActionContext, defineAction, JSONSchema7, stripUndefinedProps, @@ -188,6 +189,8 @@ export interface ToolFnOptions { * getting interrupted (immediately) and tool request returned to the upstream caller. */ interrupt: (metadata?: Record) => never; + + context: ActionContext; } export type ToolFn = ( @@ -212,9 +215,12 @@ export function defineTool( actionType: 'tool', metadata: { ...(config.metadata || {}), type: 'tool' }, }, - (i) => + (i, { context }) => fn(i, { interrupt: interruptTool, + context: { + ...context, + }, }) ); (a as ToolAction).reply = (interrupt, replyData, options) => { diff --git a/js/ai/tests/prompt/prompt_test.ts b/js/ai/tests/prompt/prompt_test.ts index eaa1d975a..f2db66e3a 100644 --- a/js/ai/tests/prompt/prompt_test.ts +++ b/js/ai/tests/prompt/prompt_test.ts @@ -14,20 +14,21 @@ * limitations under the License. */ -import { z } from '@genkit-ai/core'; +import { ActionContext, runWithContext, z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; +import { toJsonSchema } from '../../../core/src/schema'; import { Document } from '../../lib/document'; import { GenerateOptions } from '../../lib/index'; import { Session } from '../../lib/session'; -import { ModelAction, defineModel } from '../../src/model.ts'; +import { ModelAction, defineModel } from '../../src/model'; import { PromptConfig, PromptGenerateOptions, definePrompt, -} from '../../src/prompt.ts'; -import { defineTool } from '../../src/tool.ts'; +} from '../../src/prompt'; +import { defineTool } from '../../src/tool'; describe('prompt', () => { let registry; @@ -55,6 +56,7 @@ describe('prompt', () => { wantRendered?: GenerateOptions; state?: any; only?: boolean; + context?: ActionContext; }[] = [ { name: 'renders user prompt', @@ -79,6 +81,32 @@ describe('prompt', () => { model: 'echoModel', }, }, + { + name: 'renders user prompt with context', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + prompt: 'hello {{name}} ({{@state.name}}, {{@auth.email}})', + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { config: { temperature: 11 } }, + context: { auth: { email: 'a@b.c' } }, + wantTextOutput: + 'Echo: hello foo (bar, a@b.c); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [ + { content: [{ text: 'hello foo (bar, a@b.c)' }], role: 'user' }, + ], + model: 'echoModel', + }, + }, { name: 'renders user prompt with explicit messages override', prompt: { @@ -229,6 +257,69 @@ describe('prompt', () => { model: 'echoModel', }, }, + { + name: 'renders user prompt from function with context', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + prompt: async (input, { state, context }) => { + return `hello ${input.name} (${state.name}, ${context.auth?.email})`; + }, + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + context: { auth: { email: 'a@b.c' } }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: hello foo (bar, a@b.c); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [ + { content: [{ text: 'hello foo (bar, a@b.c)' }], role: 'user' }, + ], + model: 'echoModel', + }, + }, + { + name: 'renders user prompt from function with context as render option', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + prompt: async (input, { state, context }) => { + return `hello ${input.name} (${state.name}, ${context.auth?.email})`; + }, + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { + config: { temperature: 11 }, + context: { auth: { email: 'a@b.c' } }, + }, + wantTextOutput: + 'Echo: hello foo (bar, a@b.c); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + context: { + auth: { + email: 'a@b.c', + }, + }, + messages: [ + { content: [{ text: 'hello foo (bar, a@b.c)' }], role: 'user' }, + ], + model: 'echoModel', + }, + }, { name: 'renders system prompt', prompt: { @@ -252,6 +343,61 @@ describe('prompt', () => { model: 'echoModel', }, }, + { + name: 'renders system prompt with context', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + system: 'hello {{name}} ({{@state.name}}, {{@auth.email}})', + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + context: { auth: { email: 'a@b.c' } }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: system: hello foo (bar, a@b.c); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [ + { content: [{ text: 'hello foo (bar, a@b.c)' }], role: 'system' }, + ], + model: 'echoModel', + }, + }, + { + name: 'renders system prompt with context as render option', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + system: 'hello {{name}} ({{@state.name}}, {{@auth.email}})', + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { + config: { temperature: 11 }, + context: { auth: { email: 'a@b.c' } }, + }, + wantTextOutput: + 'Echo: system: hello foo (bar, a@b.c); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + context: { auth: { email: 'a@b.c' } }, + messages: [ + { content: [{ text: 'hello foo (bar, a@b.c)' }], role: 'system' }, + ], + model: 'echoModel', + }, + }, { name: 'renders system prompt from a function', prompt: { @@ -277,6 +423,65 @@ describe('prompt', () => { model: 'echoModel', }, }, + { + name: 'renders system prompt from a function with context', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + system: async (input, { state, context }) => { + return `hello ${input.name} (${state.name}, ${context.auth?.email})`; + }, + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + context: { auth: { email: 'a@b.c' } }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: system: hello foo (bar, a@b.c); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [ + { content: [{ text: 'hello foo (bar, a@b.c)' }], role: 'system' }, + ], + model: 'echoModel', + }, + }, + { + name: 'renders system prompt from a function with context as render option', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + system: async (input, { state, context }) => { + return `hello ${input.name} (${state.name}, ${context.auth?.email})`; + }, + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + inputOptions: { + config: { temperature: 11 }, + context: { auth: { email: 'a@b.c' } }, + }, + wantTextOutput: + 'Echo: system: hello foo (bar, a@b.c); config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + context: { auth: { email: 'a@b.c' } }, + messages: [ + { content: [{ text: 'hello foo (bar, a@b.c)' }], role: 'system' }, + ], + model: 'echoModel', + }, + }, { name: 'renders messages from template', prompt: { @@ -304,6 +509,34 @@ describe('prompt', () => { model: 'echoModel', }, }, + { + name: 'renders messages from template with context', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + messages: + '{{role "system"}}system {{name}}{{role "user"}}user {{@state.name}}, {{@auth.email}}', + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + context: { auth: { email: 'a@b.c' } }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: system: system foo,user bar, a@b.c; config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [ + { role: 'system', content: [{ text: 'system foo' }] }, + { role: 'user', content: [{ text: 'user bar, a@b.c' }] }, + ], + model: 'echoModel', + }, + }, { name: 'renders messages', prompt: { @@ -362,6 +595,39 @@ describe('prompt', () => { model: 'echoModel', }, }, + { + name: 'renders messages from function with context', + prompt: { + model: 'echoModel', + name: 'prompt1', + config: { banana: 'ripe' }, + input: { schema: z.object({ name: z.string() }) }, + messages: async (input, { state, context }) => [ + { role: 'system', content: [{ text: `system ${input.name}` }] }, + { + role: 'user', + content: [{ text: `user ${state.name}, ${context.auth?.email}` }], + }, + ], + }, + input: { name: 'foo' }, + state: { name: 'bar' }, + context: { auth: { email: 'a@b.c' } }, + inputOptions: { config: { temperature: 11 } }, + wantTextOutput: + 'Echo: system: system foo,user bar, a@b.c; config: {"banana":"ripe","temperature":11}', + wantRendered: { + config: { + banana: 'ripe', + temperature: 11, + }, + messages: [ + { role: 'system', content: [{ text: 'system foo' }] }, + { role: 'user', content: [{ text: 'user bar, a@b.c' }] }, + ], + model: 'echoModel', + }, + }, { name: 'renders system, message and prompt in the same order', prompt: { @@ -504,23 +770,30 @@ describe('prompt', () => { } const p = definePrompt(registry, test.prompt); - const { text } = await (session - ? session.run(() => p(test.input, test.inputOptions)) - : p(test.input, test.inputOptions)); + const sessionFn = () => + session + ? session.run(() => p(test.input, test.inputOptions)) + : p(test.input, test.inputOptions); + + const { text } = await runWithContext(registry, test.context, sessionFn); assert.strictEqual(text, test.wantTextOutput); + + const sessionRenderFn = () => + session + ? session.run(() => p.render(test.input, test.inputOptions)) + : p.render(test.input, test.inputOptions); + assert.deepStrictEqual( stripUndefined( - await (session - ? session.run(() => p.render(test.input, test.inputOptions)) - : p.render(test.input, test.inputOptions)) + await runWithContext(registry, test.context, sessionRenderFn) ), test.wantRendered ); }); } - it.skip('respects output schema in the definition', async () => { + it('respects output schema in the definition', async () => { const schema1 = z.object({ puppyName: z.string({ description: 'A cute name for a puppy' }), }); @@ -543,7 +816,10 @@ describe('prompt', () => { const generateRequest = await prompt1.render('poodle', { model: 'geminiPro', }); - assert.equal(generateRequest.output?.schema, schema1); + assert.deepStrictEqual( + toJsonSchema({ schema: generateRequest.output?.schema }), + toJsonSchema({ schema: schema1 }) + ); }); }); diff --git a/js/core/src/action.ts b/js/core/src/action.ts index 116ef9ebf..30e1ac7dd 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -16,7 +16,7 @@ import { JSONSchema7 } from 'json-schema'; import * as z from 'zod'; -import { getContext } from './context.js'; +import { ActionContext, getContext, runWithContext } from './context.js'; import { ActionType, Registry } from './registry.js'; import { parseSchema } from './schema.js'; import { @@ -70,7 +70,7 @@ export interface ActionRunOptions { /** * Additional runtime context data (ex. auth context data). */ - context?: any; + context?: ActionContext; /** * Additional span attributes to apply to OT spans. @@ -90,7 +90,7 @@ export interface ActionFnArg { /** * Additional runtime context data (ex. auth context data). */ - context?: any; + context?: ActionContext; } /** @@ -310,11 +310,19 @@ export function action< metadata.input = input; try { - const output = await fn(input, { - // Context can either be explicitly set, or inherited from the parent action. - context: options?.context ?? getContext(registry), - sendChunk: options?.onChunk ?? sentinelNoopStreamingCallback, - }); + const actionFn = () => + fn(input, { + // Context can either be explicitly set, or inherited from the parent action. + context: options?.context ?? getContext(registry), + sendChunk: options?.onChunk ?? sentinelNoopStreamingCallback, + }); + // if context is explicitly passed in, we run action with the provided context, + // otherwise we let upstream context carry through. + const output = await runWithContext( + registry, + options?.context, + actionFn + ); metadata.output = JSON.stringify(output); return output; diff --git a/js/core/src/context.ts b/js/core/src/context.ts index 50efb988e..bc68b3f2e 100644 --- a/js/core/src/context.ts +++ b/js/core/src/context.ts @@ -14,48 +14,44 @@ * limitations under the License. */ -import { AsyncLocalStorage } from 'node:async_hooks'; import { runInActionRuntimeContext } from './action.js'; import { HasRegistry, Registry } from './registry.js'; const contextAlsKey = 'core.auth.context'; -const legacyContextAsyncLocalStorage = new AsyncLocalStorage(); + +export interface ActionContext { + /** Information about the currently authenticated user if provided. */ + auth?: Record; + [additionalContext: string]: any; +} /** * Execute the provided function in the runtime context. Call {@link getFlowContext()} anywhere - * within the async call stack to retrieve the context. + * within the async call stack to retrieve the context. If context object is undefined, this function + * is a no op passthrough, the function will be invoked as is. */ export function runWithContext( registry: Registry, - context: any, + context: ActionContext | undefined, fn: () => R -) { - return legacyContextAsyncLocalStorage.run(context, () => - registry.asyncStore.run(contextAlsKey, context, () => - runInActionRuntimeContext(registry, fn) - ) - ); -} - -/** - * Gets the auth object from the current context. - * - * @deprecated use {@link getFlowContext} - */ -export function getFlowAuth(registry?: Registry | HasRegistry): any { - if (!registry) { - return legacyContextAsyncLocalStorage.getStore(); +): R { + if (context === undefined) { + return fn(); } - return getContext(registry); + return registry.asyncStore.run(contextAlsKey, context, () => + runInActionRuntimeContext(registry, fn) + ); } /** * Gets the runtime context of the current flow. */ -export function getContext(registry: Registry | HasRegistry): any { +export function getContext( + registry: Registry | HasRegistry +): ActionContext | undefined { if ((registry as HasRegistry).registry) { registry = (registry as HasRegistry).registry; } registry = registry as Registry; - return registry.asyncStore.getStore(contextAlsKey); + return registry.asyncStore.getStore(contextAlsKey); } diff --git a/js/core/src/flow.ts b/js/core/src/flow.ts index 7c416f6ec..86dab3b9a 100644 --- a/js/core/src/flow.ts +++ b/js/core/src/flow.ts @@ -17,7 +17,7 @@ import { AsyncLocalStorage } from 'node:async_hooks'; import { z } from 'zod'; import { Action, defineAction, StreamingCallback } from './action.js'; -import { runWithContext } from './context.js'; +import { ActionContext } from './context.js'; import { HasRegistry, Registry } from './registry.js'; import { runInNewSpan, SPAN_TYPE_ATTR } from './tracing.js'; @@ -64,7 +64,7 @@ export interface FlowSideChannel { /** * Additional runtime context data (ex. auth context data). */ - context?: any; + context?: ActionContext; } /** @@ -125,9 +125,7 @@ function defineFlowAction< const ctx = sendChunk; (ctx as FlowSideChannel>).sendChunk = sendChunk; (ctx as FlowSideChannel>).context = context; - return runWithContext(registry, context, () => - fn(input, ctx as FlowSideChannel>) - ); + return fn(input, ctx as FlowSideChannel>); }); } ); diff --git a/js/core/src/index.ts b/js/core/src/index.ts index b5b9000cc..1039a0cc5 100644 --- a/js/core/src/index.ts +++ b/js/core/src/index.ts @@ -29,7 +29,7 @@ export const GENKIT_REFLECTION_API_SPEC_VERSION = 1; export { z } from 'zod'; export * from './action.js'; -export { getFlowAuth } from './context.js'; +export { getContext, runWithContext, type ActionContext } from './context.js'; export { GenkitError } from './error.js'; export { defineFlow, diff --git a/js/core/tests/action_test.ts b/js/core/tests/action_test.ts index 0b4391b15..76f5c1d84 100644 --- a/js/core/tests/action_test.ts +++ b/js/core/tests/action_test.ts @@ -144,4 +144,27 @@ describe('action', () => { { count: 3 }, ]); }); + + it('should inherit context from parent action invocation', async () => { + const child = defineAction( + registry, + { name: 'child', actionType: 'custom' }, + async (_, { context }) => { + return `hi ${context.auth.email}`; + } + ); + const parent = defineAction( + registry, + { name: 'parent', actionType: 'custom' }, + async () => { + return child(); + } + ); + + const response = await parent(undefined, { + context: { auth: { email: 'a@b.c' } }, + }); + + assert.strictEqual(response, 'hi a@b.c'); + }); }); diff --git a/js/core/tests/flow_test.ts b/js/core/tests/flow_test.ts index aaeb64ef3..3decd65d7 100644 --- a/js/core/tests/flow_test.ts +++ b/js/core/tests/flow_test.ts @@ -18,7 +18,7 @@ import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { defineFlow, run } from '../src/flow.js'; -import { defineAction, getFlowAuth, z } from '../src/index.js'; +import { defineAction, getContext, z } from '../src/index.js'; import { Registry } from '../src/registry.js'; import { enableTelemetry } from '../src/tracing.js'; import { TestSpanExporter } from './utils.js'; @@ -114,7 +114,13 @@ describe('flow', () => { }); }); - describe('getFlowAuth', () => { + describe('getContext', () => { + let registry: Registry; + + beforeEach(() => { + registry = new Registry(); + }); + it('should run the flow', async () => { const testFlow = defineFlow( registry, @@ -124,7 +130,7 @@ describe('flow', () => { outputSchema: z.string(), }, async (input) => { - return `bar ${input} ${JSON.stringify(getFlowAuth())}`; + return `bar ${input} ${JSON.stringify(getContext(registry))}`; } ); @@ -150,7 +156,7 @@ describe('flow', () => { streamingCallback({ count: i }); } } - return `bar ${input} ${!!streamingCallback} ${JSON.stringify(getFlowAuth())}`; + return `bar ${input} ${!!streamingCallback} ${JSON.stringify(getContext(registry))}`; } ); @@ -178,7 +184,7 @@ describe('flow', () => { outputSchema: z.string(), }, async (input) => { - return `bar ${input} ${JSON.stringify(getFlowAuth())}`; + return `bar ${input} ${JSON.stringify(getContext(registry))}`; } ); diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index 0e2435a27..48afd4bec 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -100,6 +100,7 @@ import { import { ToolFn } from '@genkit-ai/ai/tool'; import { Action, + ActionContext, FlowConfig, FlowFn, JSONSchema, @@ -108,6 +109,7 @@ import { defineFlow, defineJsonSchema, defineSchema, + getContext, isDevEnv, run, z, @@ -785,6 +787,15 @@ export class Genkit implements HasRegistry { return run(name, funcOrInput, this.registry); } + /** + * Returns current action (or flow) invocation context. Can be used to access things like auth + * data set by HTTP server frameworks. If invoked outside of an action (e.g. flow or tool) will + * return `undefined`. + */ + currentContext(): ActionContext | undefined { + return getContext(this); + } + /** * Configures the Genkit instance. */ diff --git a/js/genkit/src/index.ts b/js/genkit/src/index.ts index 7dbb1f9cd..204b60a59 100644 --- a/js/genkit/src/index.ts +++ b/js/genkit/src/index.ts @@ -121,12 +121,12 @@ export { defineJsonSchema, defineSchema, getCurrentEnv, - getFlowAuth, getStreamingCallback, isDevEnv, runWithStreamingCallback, z, type Action, + type ActionContext, type ActionMetadata, type Flow, type FlowConfig, diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index 45e3abe55..bc44ca99b 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -435,6 +435,65 @@ describe('generate', () => { }); }); + it('should propagate context to the tool', async () => { + const schema = z.object({ + foo: z.string(), + }); + + ai.defineTool( + { + name: 'testTool', + description: 'description', + inputSchema: schema, + outputSchema: schema, + }, + async (_, { context }) => { + return { + foo: `bar ${context.auth.email}`, + }; + } + ); + + // first response be tools call, the subsequent just text response from agent b. + let reqCounter = 0; + pm.handleResponse = async (req, sc) => { + return { + message: { + role: 'model', + content: [ + reqCounter++ === 0 + ? { + toolRequest: { + name: 'testTool', + input: { foo: 'fromTool' }, + ref: 'ref123', + }, + } + : { + text: req.messages + .splice(-1) + .map((m) => + m.content + .map( + (c) => + c.text || JSON.stringify(c.toolResponse?.output) + ) + .join() + ) + .join(), + }, + ], + }, + }; + }; + const { text } = await ai.generate({ + prompt: 'call the tool', + tools: ['testTool'], + context: { auth: { email: 'a@b.c' } }, + }); + assert.strictEqual(text, '{"foo":"bar a@b.c"}'); + }); + it('streams the tool responses', async () => { ai.defineTool( { name: 'testTool', description: 'description' }, diff --git a/js/plugins/express/src/index.ts b/js/plugins/express/src/index.ts index cdc6ada22..d30e26b04 100644 --- a/js/plugins/express/src/index.ts +++ b/js/plugins/express/src/index.ts @@ -34,7 +34,7 @@ export interface AuthPolicyContext< > { action?: Action; input: z.infer; - auth: any | undefined; + auth?: Record; request: RequestWithAuth; } @@ -56,7 +56,7 @@ export interface AuthPolicy< * the flow context. */ export interface RequestWithAuth extends express.Request { - auth?: unknown; + auth?: Record; } /** @@ -116,7 +116,7 @@ export function expressHandler< () => action.run(input, { onChunk, - context: auth, + context: { auth }, }) ); response.write( @@ -140,7 +140,7 @@ export function expressHandler< } } else { try { - const result = await action.run(input, { context: auth }); + const result = await action.run(input, { context: { auth } }); response.setHeader('x-genkit-trace-id', result.telemetry.traceId); response.setHeader('x-genkit-span-id', result.telemetry.spanId); // Responses for non-streaming flows are passed back with the flow result stored in a field called "result." diff --git a/js/plugins/express/tests/express_test.ts b/js/plugins/express/tests/express_test.ts index ad6a9a958..d4c0917fb 100644 --- a/js/plugins/express/tests/express_test.ts +++ b/js/plugins/express/tests/express_test.ts @@ -82,7 +82,7 @@ describe('expressHandler', async () => { inputSchema: z.object({ question: z.string() }), }, async (input, { context }) => { - return `${input.question} - ${JSON.stringify(context)}`; + return `${input.question} - ${JSON.stringify(context.auth)}`; } ); @@ -382,7 +382,7 @@ describe('startFlowServer', async () => { inputSchema: z.object({ question: z.string() }), }, async (input, { context }) => { - return `${input.question} - ${JSON.stringify(context)}`; + return `${input.question} - ${JSON.stringify(context.auth)}`; } ); diff --git a/js/plugins/firebase/tests/functions_test.ts b/js/plugins/firebase/tests/functions_test.ts index 365daec3a..7e50635fa 100644 --- a/js/plugins/firebase/tests/functions_test.ts +++ b/js/plugins/firebase/tests/functions_test.ts @@ -81,7 +81,7 @@ describe('function', () => { authPolicy: authPolicy, }, async (input, { context }) => { - return `hi ${input} - ${JSON.stringify(context)}`; + return `hi ${input} - ${JSON.stringify(context?.auth)}`; } ); @@ -96,7 +96,7 @@ describe('function', () => { sendChunk({ chubk: 2 }); sendChunk({ chubk: 3 }); - return `hi ${input} - ${JSON.stringify(context)}`; + return `hi ${input} - ${JSON.stringify(context?.auth)}`; } ); const app = express(); diff --git a/js/testapps/express/src/index.ts b/js/testapps/express/src/index.ts index 16a3757ed..8e6cc0019 100644 --- a/js/testapps/express/src/index.ts +++ b/js/testapps/express/src/index.ts @@ -79,7 +79,7 @@ app.use(express.json()); const authPolicies: Record = { jokeFlow: ({ auth }) => { - if (auth.username != 'Ali Baba') { + if (auth?.username != 'Ali Baba') { throw new Error('unauthorized: ' + JSON.stringify(auth)); } }, From a3c6c28687bbd3641b4e83b77eba82aa11f97133 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 27 Jan 2025 08:46:03 -0500 Subject: [PATCH 272/562] refactor(js/ai): refactored constrained generation into middleware, simplified json format (#1612) --- genkit-tools/common/src/types/model.ts | 6 +- genkit-tools/genkit-schema.json | 11 +- js/ai/src/formats/json.ts | 15 +- js/ai/src/generate.ts | 11 +- js/ai/src/generate/action.ts | 35 +++- js/ai/src/model.ts | 12 +- js/ai/src/model/middleware.ts | 46 ++++- js/ai/tests/formats/json_test.ts | 69 ++++++- js/ai/tests/helpers.ts | 60 +++++++ js/ai/tests/model/middleware_test.ts | 169 ++++++++++++++++++ js/genkit/src/model.ts | 1 + js/plugins/checks/src/index.ts | 3 +- js/plugins/googleai/src/gemini.ts | 6 + js/plugins/vertexai/src/gemini.ts | 4 + js/plugins/vertexai/src/imagen.ts | 3 +- .../vertexai/src/modelgarden/anthropic.ts | 3 +- .../vertexai/src/modelgarden/mistral.ts | 4 +- .../vertexai/src/modelgarden/model_garden.ts | 8 +- js/testapps/flow-simple-ai/src/index.ts | 8 +- 19 files changed, 429 insertions(+), 45 deletions(-) create mode 100644 js/ai/tests/helpers.ts diff --git a/genkit-tools/common/src/types/model.ts b/genkit-tools/common/src/types/model.ts index 9326e6988..d1f4a74dc 100644 --- a/genkit-tools/common/src/types/model.ts +++ b/genkit-tools/common/src/types/model.ts @@ -115,9 +115,13 @@ export const ModelInfoSchema = z.object({ /** Model can accept messages with role "system". */ systemRole: z.boolean().optional(), /** Model can output this type of data. */ - output: z.array(OutputFormatSchema).optional(), + output: z.array(z.string()).optional(), + /** Model supports output in these content types. */ + contentType: z.array(z.string()).optional(), /** Model can natively support document-based context grounding. */ context: z.boolean().optional(), + /** Model can natively support constrained generation. */ + constrained: z.boolean().optional(), }) .optional(), }); diff --git a/genkit-tools/genkit-schema.json b/genkit-tools/genkit-schema.json index 16e08ded9..9cddfd60b 100644 --- a/genkit-tools/genkit-schema.json +++ b/genkit-tools/genkit-schema.json @@ -679,11 +679,20 @@ "output": { "type": "array", "items": { - "$ref": "#/$defs/GenerateRequest/properties/output/properties/format" + "type": "string" + } + }, + "contentType": { + "type": "array", + "items": { + "type": "string" } }, "context": { "type": "boolean" + }, + "constrained": { + "type": "boolean" } }, "additionalProperties": false diff --git a/js/ai/src/formats/json.ts b/js/ai/src/formats/json.ts index 529f51095..cc4beecd0 100644 --- a/js/ai/src/formats/json.ts +++ b/js/ai/src/formats/json.ts @@ -24,18 +24,7 @@ export const jsonFormatter: Formatter = { contentType: 'application/json', constrained: true, }, - handler: (schema) => { - let instructions: string | undefined; - - if (schema) { - instructions = `Output should be in JSON format and conform to the following schema: - -\`\`\` -${JSON.stringify(schema)} -\`\`\` -`; - } - + handler: () => { return { parseChunk: (chunk) => { return extractJson(chunk.accumulatedText); @@ -44,8 +33,6 @@ ${JSON.stringify(schema)} parseMessage: (message) => { return extractJson(message.text); }, - - instructions, }; }, }; diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index 8579142c3..ab83b5012 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -346,21 +346,12 @@ export async function generate< jsonSchema: resolvedOptions.output?.jsonSchema, }); - // If is schema is set but format is not explicitly set, default to `json` format. - if (resolvedOptions.output?.schema && !resolvedOptions.output?.format) { - resolvedOptions.output.format = 'json'; - } const resolvedFormat = await resolveFormat(registry, resolvedOptions.output); - const instructions = resolveInstructions( - resolvedFormat, - resolvedSchema, - resolvedOptions?.output?.instructions - ); const params: z.infer = { model: resolvedModel.modelAction.__action.name, docs: resolvedOptions.docs, - messages: injectInstructions(messages, instructions), + messages: messages, tools, toolChoice: resolvedOptions.toolChoice, config: { diff --git a/js/ai/src/generate/action.ts b/js/ai/src/generate/action.ts index 8f3601a8e..9701285ce 100644 --- a/js/ai/src/generate/action.ts +++ b/js/ai/src/generate/action.ts @@ -26,7 +26,11 @@ import { toJsonSchema } from '@genkit-ai/core/schema'; import { SPAN_TYPE_ATTR, runInNewSpan } from '@genkit-ai/core/tracing'; import * as clc from 'colorette'; import { DocumentDataSchema } from '../document.js'; -import { resolveFormat } from '../formats/index.js'; +import { + injectInstructions, + resolveFormat, + resolveInstructions, +} from '../formats/index.js'; import { Formatter } from '../formats/types.js'; import { GenerateResponse, @@ -148,10 +152,39 @@ async function generate( const tools = await resolveTools(registry, options.rawRequest.tools); + const resolvedSchema = toJsonSchema({ + jsonSchema: options.rawRequest.output?.jsonSchema, + }); + + // If is schema is set but format is not explicitly set, default to `json` format. + if ( + options.rawRequest.output?.jsonSchema && + !options.rawRequest.output?.format + ) { + options.rawRequest.output.format = 'json'; + } const resolvedFormat = await resolveFormat( registry, options.rawRequest.output ); + const instructions = resolveInstructions( + resolvedFormat, + resolvedSchema, + options.rawRequest?.output?.instructions + ); + if (resolvedFormat) { + options.rawRequest.messages = injectInstructions( + options.rawRequest.messages, + instructions + ); + options.rawRequest.output = { + // use output config from the format + ...resolvedFormat.config, + // if anything is set explicitly, use that + ...options.rawRequest.output, + }; + } + // Create a lookup of tool names with namespaces stripped to original names const toolMap = tools.reduce>((acc, tool) => { const name = tool.__action.name; diff --git a/js/ai/src/model.ts b/js/ai/src/model.ts index 9fb5f49f4..ce15adaa3 100644 --- a/js/ai/src/model.ts +++ b/js/ai/src/model.ts @@ -27,7 +27,12 @@ import { Registry } from '@genkit-ai/core/registry'; import { toJsonSchema } from '@genkit-ai/core/schema'; import { performance } from 'node:perf_hooks'; import { DocumentDataSchema } from './document.js'; -import { augmentWithContext, validateSupport } from './model/middleware.js'; +import { + augmentWithContext, + simulateConstrainedGeneration, + validateSupport, +} from './model/middleware.js'; +export { simulateConstrainedGeneration }; // // IMPORTANT: Please keep type definitions in sync with @@ -204,6 +209,8 @@ export const ModelInfoSchema = z.object({ contentType: z.array(z.string()).optional(), /** Model can natively support document-based context grounding. */ context: z.boolean().optional(), + /** Model can natively support constrained generation. */ + constrained: z.boolean().optional(), /** Model supports controlling tool choice, e.g. forced tool calling. */ toolChoice: z.boolean().optional(), }) @@ -478,7 +485,8 @@ export function defineModel< validateSupport(options), ]; if (!options?.supports?.context) middleware.push(augmentWithContext()); - // middleware.push(conformOutput(registry)); + if (!options?.supports?.constrained) + middleware.push(simulateConstrainedGeneration()); const act = defineAction( registry, { diff --git a/js/ai/src/model/middleware.ts b/js/ai/src/model/middleware.ts index 2e5530bc1..54febab05 100644 --- a/js/ai/src/model/middleware.ts +++ b/js/ai/src/model/middleware.ts @@ -15,6 +15,7 @@ */ import { Document } from '../document.js'; +import { injectInstructions } from '../formats/index.js'; import type { MediaPart, MessageData, @@ -22,7 +23,6 @@ import type { ModelMiddleware, Part, } from '../model.js'; - /** * Preprocess a GenerateRequest to download referenced http(s) media URLs and * inline them as data URIs. @@ -234,3 +234,47 @@ export function augmentWithContext( return next(req); }; } + +export interface SimulatedConstrainedGenerationOptions { + instructionsRenderer?: (schema: Record) => string; +} + +const DEFAULT_CONSTRAINED_GENERATION_INSTRUSCTIONS = ( + schema: Record +) => `Output should be in JSON format and conform to the following schema: + +\`\`\` +${JSON.stringify(schema)} +\`\`\` +`; + +/** + * Model middleware that simulates constrained generation by injecting generation + * instructions into the user message. + */ +export function simulateConstrainedGeneration( + options?: SimulatedConstrainedGenerationOptions +): ModelMiddleware { + return (req, next) => { + let instructions: string | undefined; + if (req.output?.constrained && req.output?.schema) { + instructions = ( + options?.instructionsRenderer ?? + DEFAULT_CONSTRAINED_GENERATION_INSTRUSCTIONS + )(req.output?.schema); + + req = { + ...req, + messages: injectInstructions(req.messages, instructions), + output: { + ...req.output, + // we're simulating it, so to the underlying model it's unconstrained. + constrained: false, + schema: undefined, + }, + }; + } + + return next(req); + }; +} diff --git a/js/ai/tests/formats/json_test.ts b/js/ai/tests/formats/json_test.ts index 9dd5723a3..70c8a733b 100644 --- a/js/ai/tests/formats/json_test.ts +++ b/js/ai/tests/formats/json_test.ts @@ -14,14 +14,30 @@ * limitations under the License. */ -import * as assert from 'assert'; -import { describe, it } from 'node:test'; +import { z } from '@genkit-ai/core'; +import { Registry } from '@genkit-ai/core/registry'; +import assert from 'node:assert'; +import { beforeEach, describe, it } from 'node:test'; +import { configureFormats } from '../../src/formats/index.js'; import { jsonFormatter } from '../../src/formats/json.js'; -import { GenerateResponseChunk } from '../../src/generate.js'; +import { GenerateResponseChunk, generateStream } from '../../src/generate.js'; import { Message } from '../../src/message.js'; import { GenerateResponseChunkData, MessageData } from '../../src/model.js'; +import { + ProgrammableModel, + defineProgrammableModel, + runAsync, +} from '../helpers.js'; describe('jsonFormat', () => { + let registry: Registry; + let pm: ProgrammableModel; + + beforeEach(() => { + registry = new Registry(); + pm = defineProgrammableModel(registry); + }); + const streamingTests = [ { desc: 'parses complete JSON object', @@ -123,3 +139,50 @@ describe('jsonFormat', () => { }); } }); + +describe('jsonFormat e2e', () => { + let registry: Registry; + + beforeEach(() => { + registry = new Registry(); + configureFormats(registry); + }); + + it('injects the instructions into the request', async () => { + let pm = defineProgrammableModel(registry); + pm.handleResponse = async (req, sc) => { + await runAsync(() => sc?.({ content: [{ text: '```\n{' }] })); + await runAsync(() => sc?.({ content: [{ text: '"foo": "b' }] })); + await runAsync(() => sc?.({ content: [{ text: 'ar"' }] })); + await runAsync(() => sc?.({ content: [{ text: '}\n```"' }] })); + return await runAsync(() => ({ + message: { + role: 'model', + content: [{ text: '```\n{"foo": "bar"}\n```' }], + }, + })); + }; + + const { response, stream } = await generateStream(registry, { + model: 'programmableModel', + prompt: 'generate json', + output: { + format: 'json', + schema: z.object({ + foo: z.string(), + }), + }, + }); + const chunks: any = []; + for await (const chunk of stream) { + chunks.push(chunk.output); + } + assert.deepEqual((await response).output, { foo: 'bar' }); + assert.deepStrictEqual(chunks, [ + {}, + { foo: 'b' }, + { foo: 'bar' }, + { foo: 'bar' }, + ]); + }); +}); diff --git a/js/ai/tests/helpers.ts b/js/ai/tests/helpers.ts new file mode 100644 index 000000000..174833be3 --- /dev/null +++ b/js/ai/tests/helpers.ts @@ -0,0 +1,60 @@ +/** + * Copyright 2024 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 { StreamingCallback } from '@genkit-ai/core'; +import { Registry } from '@genkit-ai/core/registry'; +import { + GenerateRequest, + GenerateResponseChunkData, + GenerateResponseData, + ModelAction, + ModelInfo, + defineModel, +} from '../src/model'; + +export async function runAsync(fn: () => O): Promise { + return new Promise((resolve) => { + setTimeout(() => resolve(fn()), 0); + }); +} + +export type ProgrammableModel = ModelAction & { + handleResponse: ( + req: GenerateRequest, + streamingCallback?: StreamingCallback + ) => Promise; + + lastRequest?: GenerateRequest; +}; + +export function defineProgrammableModel( + registry: Registry, + info?: ModelInfo +): ProgrammableModel { + const pm = defineModel( + registry, + { + ...info, + name: 'programmableModel', + }, + async (request, streamingCallback) => { + pm.lastRequest = JSON.parse(JSON.stringify(request)); + return pm.handleResponse(request, streamingCallback); + } + ) as ProgrammableModel; + + return pm; +} diff --git a/js/ai/tests/model/middleware_test.ts b/js/ai/tests/model/middleware_test.ts index 8b2156d71..468e08903 100644 --- a/js/ai/tests/model/middleware_test.ts +++ b/js/ai/tests/model/middleware_test.ts @@ -14,11 +14,13 @@ * limitations under the License. */ +import { z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { DocumentData } from '../../src/document.js'; import { configureFormats } from '../../src/formats/index.js'; +import { generate } from '../../src/generate.js'; import { GenerateRequest, GenerateResponseData, @@ -29,9 +31,11 @@ import { AugmentWithContextOptions, CONTEXT_PREFACE, augmentWithContext, + simulateConstrainedGeneration, simulateSystemPrompt, validateSupport, } from '../../src/model/middleware.js'; +import { defineProgrammableModel } from '../helpers.js'; describe('validateSupport', () => { const examples: Record = { @@ -391,3 +395,168 @@ describe('augmentWithContext', () => { }); }); }); + +describe('simulateConstrainedGeneration', () => { + let registry: Registry; + + beforeEach(() => { + registry = new Registry(); + configureFormats(registry); + }); + + it('injects the instructions into the request', async () => { + let pm = defineProgrammableModel(registry); + pm.handleResponse = async (req, sc) => { + return { + message: { + role: 'model', + content: [{ text: '```\n{"foo": "bar"}\n```' }], + }, + }; + }; + + const { output } = await generate(registry, { + model: 'programmableModel', + prompt: 'generate json', + output: { + schema: z.object({ + foo: z.string(), + }), + format: 'json', + }, + }); + assert.deepEqual(output, { foo: 'bar' }); + assert.deepStrictEqual(pm.lastRequest, { + config: {}, + messages: [ + { + role: 'user', + content: [ + { text: 'generate json' }, + { + metadata: { + purpose: 'output', + }, + text: + 'Output should be in JSON format and conform to the following schema:\n' + + '\n' + + '```\n' + + '{"type":"object","properties":{"foo":{"type":"string"}},"required":["foo"],"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"}\n' + + '```\n', + }, + ], + }, + ], + output: { + constrained: false, + contentType: 'application/json', + format: 'json', + }, + tools: [], + }); + }); + + it('injects the instructions into the request idempotently', async () => { + let pm = defineProgrammableModel(registry); + pm.handleResponse = async (req, sc) => { + return { + message: { + role: 'model', + content: [{ text: '```\n{"foo": "bar"}\n```' }], + }, + }; + }; + + const { output } = await generate(registry, { + model: 'programmableModel', + prompt: 'generate json', + use: [ + simulateConstrainedGeneration({ + instructionsRenderer: (schema) => + `must be json: ${JSON.stringify(schema)}`, + }), + ], + output: { + schema: z.object({ + foo: z.string(), + }), + format: 'json', + }, + }); + assert.deepEqual(output, { foo: 'bar' }); + assert.deepStrictEqual(pm.lastRequest, { + config: {}, + messages: [ + { + role: 'user', + content: [ + { text: 'generate json' }, + { + metadata: { + purpose: 'output', + }, + text: 'must be json: {"type":"object","properties":{"foo":{"type":"string"}},"required":["foo"],"additionalProperties":true,"$schema":"http://json-schema.org/draft-07/schema#"}', + }, + ], + }, + ], + output: { + constrained: false, + contentType: 'application/json', + format: 'json', + }, + tools: [], + }); + }); + + it('relies on native support -- no instructions', async () => { + let pm = defineProgrammableModel(registry, { + supports: { constrained: true }, + }); + pm.handleResponse = async (req, sc) => { + return { + message: { + role: 'model', + content: [{ text: '```\n{"foo": "bar"}\n```' }], + }, + }; + }; + + const { output } = await generate(registry, { + model: 'programmableModel', + prompt: 'generate json', + output: { + schema: z.object({ + foo: z.string(), + }), + }, + }); + assert.deepEqual(output, { foo: 'bar' }); + assert.deepStrictEqual(pm.lastRequest, { + config: {}, + messages: [ + { + role: 'user', + content: [{ text: 'generate json' }], + }, + ], + output: { + constrained: true, + contentType: 'application/json', + format: 'json', + schema: { + $schema: 'http://json-schema.org/draft-07/schema#', + additionalProperties: true, + properties: { + foo: { + type: 'string', + }, + }, + required: ['foo'], + type: 'object', + }, + }, + tools: [], + }); + }); +}); diff --git a/js/genkit/src/model.ts b/js/genkit/src/model.ts index b662acd77..46d2f3379 100644 --- a/js/genkit/src/model.ts +++ b/js/genkit/src/model.ts @@ -37,6 +37,7 @@ export { ToolResponsePartSchema, getBasicUsageStats, modelRef, + simulateConstrainedGeneration, type CandidateData, type CandidateError, type CustomPart, diff --git a/js/plugins/checks/src/index.ts b/js/plugins/checks/src/index.ts index cd1a96434..1e17d3e8b 100644 --- a/js/plugins/checks/src/index.ts +++ b/js/plugins/checks/src/index.ts @@ -16,6 +16,7 @@ import { Genkit } from 'genkit'; import { logger } from 'genkit/logging'; +import { ModelMiddleware } from 'genkit/model'; import { GenkitPlugin, genkitPlugin } from 'genkit/plugin'; import { GoogleAuth, GoogleAuthOptions } from 'google-auth-library'; import { checksEvaluators } from './evaluation.js'; @@ -69,7 +70,7 @@ export function checks(options?: PluginOptions): GenkitPlugin { export function checksMiddleware(options: { authOptions: GoogleAuthOptions; metrics: ChecksEvaluationMetric[]; -}) { +}): ModelMiddleware { const googleAuth = inititializeAuth(options.authOptions); return authorizedMiddleware({ diff --git a/js/plugins/googleai/src/gemini.ts b/js/plugins/googleai/src/gemini.ts index 0cc6cee10..cace611fe 100644 --- a/js/plugins/googleai/src/gemini.ts +++ b/js/plugins/googleai/src/gemini.ts @@ -105,6 +105,7 @@ export const gemini10Pro = modelRef({ tools: true, toolChoice: true, systemRole: true, + constrained: true, }, }, configSchema: GeminiConfigSchema, @@ -120,6 +121,7 @@ export const gemini15Pro = modelRef({ tools: true, toolChoice: true, systemRole: true, + constrained: true, }, versions: [ 'gemini-1.5-pro-latest', @@ -140,6 +142,7 @@ export const gemini15Flash = modelRef({ tools: true, toolChoice: true, systemRole: true, + constrained: true, // @ts-ignore contextCache: true, }, @@ -162,6 +165,7 @@ export const gemini15Flash8b = modelRef({ tools: true, toolChoice: true, systemRole: true, + constrained: true, }, versions: ['gemini-1.5-flash-8b-latest', 'gemini-1.5-flash-8b-001'], }, @@ -179,6 +183,7 @@ export const gemini20FlashExp = modelRef({ tools: true, toolChoice: true, systemRole: true, + constrained: true, }, }, configSchema: GeminiConfigSchema, @@ -206,6 +211,7 @@ export const GENERIC_GEMINI_MODEL = modelRef({ tools: true, toolChoice: true, systemRole: true, + constrained: true, }, }, }); diff --git a/js/plugins/vertexai/src/gemini.ts b/js/plugins/vertexai/src/gemini.ts index d65f00479..9281173b4 100644 --- a/js/plugins/vertexai/src/gemini.ts +++ b/js/plugins/vertexai/src/gemini.ts @@ -210,6 +210,7 @@ export const gemini10Pro = modelRef({ media: false, tools: true, systemRole: true, + constrained: true, toolChoice: true, }, }, @@ -227,6 +228,7 @@ export const gemini15Pro = modelRef({ tools: true, toolChoice: true, systemRole: true, + constrained: true, }, }, configSchema: GeminiConfigSchema, @@ -243,6 +245,7 @@ export const gemini15Flash = modelRef({ tools: true, toolChoice: true, systemRole: true, + constrained: true, }, }, configSchema: GeminiConfigSchema, @@ -259,6 +262,7 @@ export const gemini20FlashExp = modelRef({ tools: true, toolChoice: true, systemRole: true, + constrained: true, }, }, configSchema: GeminiConfigSchema, diff --git a/js/plugins/vertexai/src/imagen.ts b/js/plugins/vertexai/src/imagen.ts index 3b71ba107..6f8508e78 100644 --- a/js/plugins/vertexai/src/imagen.ts +++ b/js/plugins/vertexai/src/imagen.ts @@ -19,6 +19,7 @@ import { CandidateData, GenerateRequest, GenerationCommonConfigSchema, + ModelAction, ModelReference, getBasicUsageStats, modelRef, @@ -218,7 +219,7 @@ export function imagenModel( name: string, client: GoogleAuth, options: PluginOptions -) { +): ModelAction { const modelName = `vertexai/${name}`; const model: ModelReference = SUPPORTED_IMAGEN_MODELS[name]; if (!model) throw new Error(`Unsupported model: ${name}`); diff --git a/js/plugins/vertexai/src/modelgarden/anthropic.ts b/js/plugins/vertexai/src/modelgarden/anthropic.ts index 0c967c31f..850f393a5 100644 --- a/js/plugins/vertexai/src/modelgarden/anthropic.ts +++ b/js/plugins/vertexai/src/modelgarden/anthropic.ts @@ -42,6 +42,7 @@ import { } from 'genkit'; import { GenerationCommonConfigSchema, + ModelAction, getBasicUsageStats, modelRef, } from 'genkit/model'; @@ -381,7 +382,7 @@ export function anthropicModel( modelName: string, projectId: string, region: string -) { +): ModelAction { const clients: Record = {}; const clientFactory = (region: string): AnthropicVertex => { if (!clients[region]) { diff --git a/js/plugins/vertexai/src/modelgarden/mistral.ts b/js/plugins/vertexai/src/modelgarden/mistral.ts index b2e1596de..05c04f8a4 100644 --- a/js/plugins/vertexai/src/modelgarden/mistral.ts +++ b/js/plugins/vertexai/src/modelgarden/mistral.ts @@ -42,7 +42,7 @@ import { ToolRequestPart, z, } from 'genkit'; -import { modelRef } from 'genkit/model'; +import { ModelAction, modelRef } from 'genkit/model'; export const MistralConfigSchema = GenerationCommonConfigSchema.extend({ location: z.string().optional(), @@ -323,7 +323,7 @@ export function mistralModel( modelName: string, projectId: string, region: string -) { +): ModelAction { const getClient = createClientFactory(projectId); const model = SUPPORTED_MISTRAL_MODELS[modelName]; diff --git a/js/plugins/vertexai/src/modelgarden/model_garden.ts b/js/plugins/vertexai/src/modelgarden/model_garden.ts index eec87274c..fbfcffe3b 100644 --- a/js/plugins/vertexai/src/modelgarden/model_garden.ts +++ b/js/plugins/vertexai/src/modelgarden/model_garden.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Genkit, GENKIT_CLIENT_HEADER, z } from 'genkit'; +import { Genkit, GENKIT_CLIENT_HEADER, ModelReference, z } from 'genkit'; import { GenerateRequest, ModelAction, modelRef } from 'genkit/model'; import { GoogleAuth } from 'google-auth-library'; import OpenAI from 'openai'; @@ -45,7 +45,7 @@ export const llama31 = modelRef({ }, configSchema: ModelGardenModelConfigSchema, version: 'meta/llama3-405b-instruct-maas', -}); +}) as ModelReference; export const llama32 = modelRef({ name: 'vertexai/llama-3.2', @@ -62,7 +62,7 @@ export const llama32 = modelRef({ }, configSchema: ModelGardenModelConfigSchema, version: 'meta/llama-3.2-90b-vision-instruct-maas', -}); +}) as ModelReference; /** * @deprecated use `llama31` instead @@ -82,7 +82,7 @@ export const llama3 = modelRef({ }, configSchema: ModelGardenModelConfigSchema, version: 'meta/llama3-405b-instruct-maas', -}); +}) as ModelReference; export const SUPPORTED_OPENAI_FORMAT_MODELS = { 'llama3-405b': llama3, diff --git a/js/testapps/flow-simple-ai/src/index.ts b/js/testapps/flow-simple-ai/src/index.ts index 031db5e70..a73afda44 100644 --- a/js/testapps/flow-simple-ai/src/index.ts +++ b/js/testapps/flow-simple-ai/src/index.ts @@ -28,7 +28,7 @@ import { initializeApp } from 'firebase-admin/app'; import { getFirestore } from 'firebase-admin/firestore'; import { GenerateResponseData, MessageSchema, genkit, z } from 'genkit'; import { logger } from 'genkit/logging'; -import { ModelMiddleware } from 'genkit/model'; +import { ModelMiddleware, simulateConstrainedGeneration } from 'genkit/model'; import { PluginProvider } from 'genkit/plugin'; import { Allow, parse } from 'partial-json'; @@ -249,14 +249,16 @@ export const jokeWithOutputFlow = ai.defineFlow( }), outputSchema, }, - async (input) => { + async (input, { sendChunk }) => { const llmResponse = await ai.generate({ model: input.modelName, output: { format: 'json', schema: outputSchema, }, - prompt: `Tell a joke about ${input.subject}.`, + prompt: `Tell a long joke about ${input.subject}.`, + use: [simulateConstrainedGeneration()], + onChunk: (c) => sendChunk(c.output), }); return { ...llmResponse.output! }; } From aff711981ef92b189d25a5e134b783e3643d01c0 Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Mon, 27 Jan 2025 10:39:50 -0500 Subject: [PATCH 273/562] feat: more specific errors for evals (#1626) --- genkit-tools/cli/src/commands/eval-flow.ts | 15 ++++++++++++++- genkit-tools/cli/src/commands/eval-run.ts | 7 +++++++ genkit-tools/common/src/eval/evaluate.ts | 7 +++++++ genkit-tools/common/src/utils/eval.ts | 11 +++++++++++ js/tsconfig.json | 2 +- 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/genkit-tools/cli/src/commands/eval-flow.ts b/genkit-tools/cli/src/commands/eval-flow.ts index 16b52affe..68eb8a38a 100644 --- a/genkit-tools/cli/src/commands/eval-flow.ts +++ b/genkit-tools/cli/src/commands/eval-flow.ts @@ -31,6 +31,7 @@ import { } from '@genkit-ai/tools-common/eval'; import { confirmLlmUse, + hasAction, loadInferenceDatasetFile, logger, } from '@genkit-ai/tools-common/utils'; @@ -87,12 +88,18 @@ export const evalFlow = new Command('eval:flow') .action( async (flowName: string, data: string, options: EvalFlowRunCliOptions) => { await runWithManager(async (manager) => { + const actionRef = `/flow/${flowName}`; if (!data && !options.input) { throw new Error( 'No input data passed. Specify input data using [data] argument or --input option' ); } + const hasTargetAction = await hasAction({ manager, actionRef }); + if (!hasTargetAction) { + throw new Error(`Cannot find action ${actionRef}.`); + } + let evaluatorActions: Action[]; if (!options.evaluators) { evaluatorActions = await getAllEvaluatorActions(manager); @@ -105,6 +112,13 @@ export const evalFlow = new Command('eval:flow') evalActionKeys ); } + if (!evaluatorActions.length) { + throw new Error( + options.evaluators + ? `No matching evaluators found for '${options.evaluators}'` + : `No evaluators found in your app` + ); + } logger.debug( `Using evaluators: ${evaluatorActions.map((action) => action.name).join(',')}` ); @@ -126,7 +140,6 @@ export const evalFlow = new Command('eval:flow') ); } - const actionRef = `/flow/${flowName}`; const inferenceDataset = await readInputs( sourceType, data, diff --git a/genkit-tools/cli/src/commands/eval-run.ts b/genkit-tools/cli/src/commands/eval-run.ts index f09924d55..f6e838474 100644 --- a/genkit-tools/cli/src/commands/eval-run.ts +++ b/genkit-tools/cli/src/commands/eval-run.ts @@ -78,6 +78,13 @@ export const evalRun = new Command('eval:run') evalActionKeys ); } + if (!evaluatorActions.length) { + throw new Error( + options.evaluators + ? `No matching evaluators found for '${options.evaluators}'` + : `No evaluators found in your app` + ); + } logger.info( `Using evaluators: ${evaluatorActions.map((action) => action.name).join(',')}` ); diff --git a/genkit-tools/common/src/eval/evaluate.ts b/genkit-tools/common/src/eval/evaluate.ts index da9f8a903..eeecff529 100644 --- a/genkit-tools/common/src/eval/evaluate.ts +++ b/genkit-tools/common/src/eval/evaluate.ts @@ -37,6 +37,7 @@ import { evaluatorName, generateTestCaseId, getEvalExtractors, + hasAction, isEvaluator, logger, stackTraceSpans, @@ -73,8 +74,14 @@ export async function runNewEvaluation( throw new Error(`Either 'data' or 'datasetId' must be provided`); } + const hasTargetAction = await hasAction({ manager, actionRef }); + if (!hasTargetAction) { + throw new Error(`Cannot find action ${actionRef}.`); + } + let inferenceDataset: Dataset; let metadata = {}; + if (datasetId) { const datasetStore = await getDatasetStore(); logger.info(`Fetching dataset ${datasetId}...`); diff --git a/genkit-tools/common/src/utils/eval.ts b/genkit-tools/common/src/utils/eval.ts index 1ba74f224..1c3aa4bc7 100644 --- a/genkit-tools/common/src/utils/eval.ts +++ b/genkit-tools/common/src/utils/eval.ts @@ -19,6 +19,7 @@ import { createReadStream } from 'fs'; import { readFile } from 'fs/promises'; import * as inquirer from 'inquirer'; import { createInterface } from 'readline'; +import { RuntimeManager } from '../manager'; import { EvalField, EvaluationExtractor, @@ -319,3 +320,13 @@ async function readLines(fileName: string): Promise { } return lines; } + +export async function hasAction(params: { + manager: RuntimeManager; + actionRef: string; +}): Promise { + const { manager, actionRef } = { ...params }; + const actionsRecord = await manager.listActions(); + + return actionsRecord.hasOwnProperty(actionRef); +} diff --git a/js/tsconfig.json b/js/tsconfig.json index 8198b8d7b..5ec0327e6 100644 --- a/js/tsconfig.json +++ b/js/tsconfig.json @@ -15,7 +15,7 @@ "esModuleInterop": false, "strict": true, "target": "es2022", - "lib": ["es2022"], + "lib": ["es2022", "DOM"], "noEmit": true }, "compileOnSave": true From 661f77842cedd88943c700bacc42bac291224fcf Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 27 Jan 2025 13:25:33 -0500 Subject: [PATCH 274/562] fix(js/core/async): reject channel ready promise on channel error (#1666) --- js/core/src/async.ts | 6 ++++-- js/genkit/tests/generate_test.ts | 23 ++++++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/js/core/src/async.ts b/js/core/src/async.ts index 6d1f9cc7b..84c872b09 100644 --- a/js/core/src/async.ts +++ b/js/core/src/async.ts @@ -54,9 +54,11 @@ export class Channel implements AsyncIterable { } error(err: unknown): void { - // Note: we cannot use this.ready.reject because it will be ignored - // if ready.resolved has already been called. this.err = err; + // Note: we must call this.ready.reject here in case we get an error even before the stream is initiated, + // however we cannot rely on this.ready.reject because it will be ignored if ready.resolved has already + // been called, so this.err will be checked in the iterator as well. + this.ready.reject(err); } [Symbol.asyncIterator](): AsyncIterator { diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index bc44ca99b..c6337d6ab 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -150,7 +150,7 @@ describe('generate', () => { ai = genkit({}); }); - it('rethrows errors', async () => { + it('rethrows response errors', async () => { ai.defineModel( { name: 'blockingModel', @@ -195,7 +195,7 @@ describe('generate', () => { } ); - assert.rejects(async () => { + await assert.rejects(async () => { const { response, stream } = ai.generateStream({ prompt: 'hi', model: 'blockingModel', @@ -207,6 +207,23 @@ describe('generate', () => { }); }); + it('rethrows initialization errors', async () => { + await assert.rejects( + async () => { + const { stream } = ai.generateStream({ + prompt: 'hi', + model: 'modelNotFound', + }); + for await (const chunk of stream) { + // nothing + } + }, + (e: Error) => { + return e.message.includes('Model modelNotFound not found'); + } + ); + }); + it('passes the streaming callback to the model', async () => { const model = defineEchoModel(ai); const flow = ai.defineFlow('wrapper', async (_, streamingCallback) => { @@ -449,7 +466,7 @@ describe('generate', () => { }, async (_, { context }) => { return { - foo: `bar ${context.auth.email}`, + foo: `bar ${context.auth?.email}`, }; } ); From 44ed9c72a95d0b0e21e1965463d1800c08fb4024 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:49:39 +0000 Subject: [PATCH 275/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.6 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 7b23e6f0b..fa3b9c5e1 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 28bb3e75dc122c00bb2605635ede02d4166b486d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:49:41 +0000 Subject: [PATCH 276/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.6 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 1af4b9527..1da5b7c96 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From 3899dfc3d1f72d750f80f5e8b1e3d965e2a7ab58 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:49:44 +0000 Subject: [PATCH 277/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.6 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index 372b2377d..41971412d 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From cd01f015963e6b2fb07b83206dcbc29666d7611d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:50:49 +0000 Subject: [PATCH 278/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.6 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index ad6a9b791..8d67ce264 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From c99e24ee0e39861c4cad1f3aff0afb956a3323dc Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:50:54 +0000 Subject: [PATCH 279/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.6 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index dd1c1d953..b806d6b5e 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From 4834523d2b74be97573f074b8d9284939114e9cf Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:50:56 +0000 Subject: [PATCH 280/562] chore: bump genkit version to genkit@1.0.0-rc.6 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index 6179fed8f..e2b5cb66d 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From b355eca89e55c7307deeebe16eb2cd9241adf7ea Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:51:02 +0000 Subject: [PATCH 281/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.6 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index b04712dd8..652289b85 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From 34fe7968c7369e9b45238e6039a54b02402a33da Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:51:07 +0000 Subject: [PATCH 282/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.6 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index cd65a928a..5ab5953d1 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From 525a3823a4b66f9c47a0e6d768928492f2b75079 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:51:09 +0000 Subject: [PATCH 283/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.6 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 6c50d3d73..06aba01d4 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From 75cfe236eeb86e278fbe9410a62451c1beec76f2 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:51:13 +0000 Subject: [PATCH 284/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.6 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 7018caec5..58e4b6cdf 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From 725d1d19132bf21943dcb318639c15ebdbb61d53 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:51:16 +0000 Subject: [PATCH 285/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.6 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index 4863423fb..e8f73aefd 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From 7716930564612b1c462cb9708345eff405b836c2 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:51:20 +0000 Subject: [PATCH 286/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.6 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index daab344dd..28a550d73 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From 475853bf16d7499072a635c1222bdf6affc7e91e Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:51:23 +0000 Subject: [PATCH 287/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.6 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index 1bd3353cd..9169052e8 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From 8fb1ee59d9d8fd54b93cb922ec4d4667e452e118 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:51:25 +0000 Subject: [PATCH 288/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.6 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index 317353a0b..2894c471c 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From 06b4c3bb37f6213cd6ae4b051404cc684eaaac80 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:51:28 +0000 Subject: [PATCH 289/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.6 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index 559d052d7..ea6c45176 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From ea092982b68cc3aad4e8e59a4b1ffe8c9b8c4dec Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:51:30 +0000 Subject: [PATCH 290/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.6 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 7bc8a06d0..11180a8e6 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From ec151784214e59ddf0cc2b6b51b30734a05fbca7 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:51:34 +0000 Subject: [PATCH 291/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.6 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index 64f36abbb..ad3116477 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From 5c129f1b63ebffb6325a3e7e0a0946d552ae1096 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:51:37 +0000 Subject: [PATCH 292/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.6 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index 03e12e340..549516e60 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From 0087215c970f14ee5cd4b9c9dc87c36e1396a4c8 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Mon, 27 Jan 2025 20:51:39 +0000 Subject: [PATCH 293/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.6 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index 5ae6ce3f0..0ccc4d99b 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.5", + "version": "1.0.0-rc.6", "type": "commonjs", "scripts": { "check": "tsc", From 8e553dca16c8668dcb0b590c592ede17570ab6b1 Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Mon, 27 Jan 2025 16:38:10 -0500 Subject: [PATCH 294/562] feat(evals): add new API for data validation (#1647) --- genkit-tools/common/package.json | 2 + genkit-tools/common/src/eval/index.ts | 1 + genkit-tools/common/src/eval/validate.ts | 111 +++++++++++++++++++++++ genkit-tools/common/src/server/router.ts | 16 +++- genkit-tools/common/src/types/apis.ts | 26 ++++++ genkit-tools/pnpm-lock.yaml | 50 +++++++++- 6 files changed, 202 insertions(+), 4 deletions(-) create mode 100644 genkit-tools/common/src/eval/validate.ts diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index fa3b9c5e1..b92e6ed3a 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -26,6 +26,8 @@ "json-2-csv": "^5.5.1", "json-schema": "^0.4.0", "terminate": "^2.6.1", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", "tsx": "^4.19.2", "uuid": "^9.0.1", "winston": "^3.11.0", diff --git a/genkit-tools/common/src/eval/index.ts b/genkit-tools/common/src/eval/index.ts index d2805447a..64a5db61f 100644 --- a/genkit-tools/common/src/eval/index.ts +++ b/genkit-tools/common/src/eval/index.ts @@ -21,6 +21,7 @@ export { InferenceDataset, InferenceDatasetSchema } from '../types/eval'; export * from './evaluate'; export * from './exporter'; export * from './parser'; +export * from './validate'; export function getEvalStore(): EvalStore { // TODO: This should provide EvalStore, based on tools config. diff --git a/genkit-tools/common/src/eval/validate.ts b/genkit-tools/common/src/eval/validate.ts new file mode 100644 index 000000000..2cf0f0bf3 --- /dev/null +++ b/genkit-tools/common/src/eval/validate.ts @@ -0,0 +1,111 @@ +/** + * 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. + */ + +import Ajv, { ErrorObject, JSONSchemaType } from 'ajv'; +import addFormats from 'ajv-formats'; +import { getDatasetStore } from '.'; +import { RuntimeManager } from '../manager'; +import { + Action, + ErrorDetail, + InferenceDatasetSchema, + ValidateDataRequest, + ValidateDataResponse, +} from '../types'; + +// Setup for AJV +type JSONSchema = JSONSchemaType | any; +const ajv = new Ajv(); +addFormats(ajv); + +/** + * Validate given data against a target action. Intended to be used via the + * reflection API. + */ +export async function validateSchema( + manager: RuntimeManager, + request: ValidateDataRequest +): Promise { + const { dataSource, actionRef } = request; + const { datasetId, data } = dataSource; + if (!datasetId && !data) { + throw new Error(`Either 'data' or 'datasetId' must be provided`); + } + const targetAction = await getAction(manager, actionRef); + const targetSchema = targetAction?.inputSchema; + if (!targetAction) { + throw new Error(`Could not find matching action for ${actionRef}`); + } + if (!targetSchema) { + return { valid: true }; + } + + const errorsMap: Record = {}; + + if (datasetId) { + const datasetStore = await getDatasetStore(); + const dataset = await datasetStore.getDataset(datasetId); + if (dataset.length === 0) { + return { valid: true }; + } + dataset.forEach((sample, index) => { + const response = validate(targetSchema, sample.input); + if (!response.valid) { + errorsMap[sample.testCaseId] = response.errors ?? []; + } + }); + + return Object.keys(errorsMap).length === 0 + ? { valid: true } + : { valid: false, errors: errorsMap }; + } else { + const dataset = InferenceDatasetSchema.parse(data); + dataset.forEach((sample, index) => { + const response = validate(targetSchema, sample.input); + if (!response.valid) { + errorsMap[index.toString()] = response.errors ?? []; + } + }); + return Object.keys(errorsMap).length === 0 + ? { valid: true } + : { valid: false, errors: errorsMap }; + } +} + +function validate( + jsonSchema: JSONSchema, + data: unknown +): { valid: boolean; errors?: ErrorDetail[] } { + const validator = ajv.compile(jsonSchema); + const valid = validator(data) as boolean; + const errors = validator.errors?.map((e) => e); + return { valid, errors: errors?.map(toErrorDetail) }; +} + +function toErrorDetail(error: ErrorObject): ErrorDetail { + return { + path: error.instancePath.substring(1).replace(/\//g, '.') || '(root)', + message: error.message!, + }; +} + +async function getAction( + manager: RuntimeManager, + actionRef: string +): Promise { + const actions = await manager.listActions(); + return actions[actionRef]; +} diff --git a/genkit-tools/common/src/server/router.ts b/genkit-tools/common/src/server/router.ts index dda959011..5bff7fe24 100644 --- a/genkit-tools/common/src/server/router.ts +++ b/genkit-tools/common/src/server/router.ts @@ -15,7 +15,12 @@ */ import { initTRPC, TRPCError } from '@trpc/server'; import { z } from 'zod'; -import { getDatasetStore, getEvalStore, runNewEvaluation } from '../eval'; +import { + getDatasetStore, + getEvalStore, + runNewEvaluation, + validateSchema, +} from '../eval'; import { RuntimeManager } from '../manager/manager'; import { GenkitToolsError, RuntimeInfo } from '../manager/types'; import { Action } from '../types/action'; @@ -239,6 +244,15 @@ export const TOOLS_SERVER_ROUTER = (manager: RuntimeManager) => return response; }), + /** Validate given data against a target action schema */ + validateDatasetSchema: loggedProcedure + .input(apis.ValidateDataRequestSchema) + .output(apis.ValidateDataResponseSchema) + .mutation(async ({ input }) => { + const response = await validateSchema(manager, input); + return response; + }), + /** Send a screen view analytics event */ sendPageView: t.procedure .input(apis.PageViewSchema) diff --git a/genkit-tools/common/src/types/apis.ts b/genkit-tools/common/src/types/apis.ts index f9a34cc25..5ae804549 100644 --- a/genkit-tools/common/src/types/apis.ts +++ b/genkit-tools/common/src/types/apis.ts @@ -150,3 +150,29 @@ export const RunNewEvaluationRequestSchema = z.object({ export type RunNewEvaluationRequest = z.infer< typeof RunNewEvaluationRequestSchema >; + +export const ValidateDataRequestSchema = z.object({ + dataSource: z.object({ + datasetId: z.string().optional(), + data: InferenceDatasetSchema.optional(), + }), + actionRef: z.string(), +}); +export type ValidateDataRequest = z.infer; + +export const ErrorDetailSchema = z.object({ + path: z.string(), + message: z.string(), +}); +export type ErrorDetail = z.infer; + +export const ValidateDataResponseSchema = z.object({ + valid: z.boolean(), + errors: z + .record(z.string(), z.array(ErrorDetailSchema)) + .describe( + 'Errors mapping, if any. The key is testCaseId if source is a dataset, otherewise it is the index number (stringified)' + ) + .optional(), +}); +export type ValidateDataResponse = z.infer; diff --git a/genkit-tools/pnpm-lock.yaml b/genkit-tools/pnpm-lock.yaml index de5c1e102..a12afd419 100644 --- a/genkit-tools/pnpm-lock.yaml +++ b/genkit-tools/pnpm-lock.yaml @@ -83,7 +83,7 @@ importers: version: 29.7.0(@types/node@20.12.7)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.4.5)) ts-jest: specifier: ^29.1.2 - version: 29.1.2(@babel/core@7.24.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.5))(jest@29.7.0(@types/node@20.12.7))(typescript@5.4.5) + version: 29.1.2(@babel/core@7.24.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.5))(jest@29.7.0(@types/node@20.12.7)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.4.5)))(typescript@5.4.5) typescript: specifier: ^5.3.3 version: 5.4.5 @@ -99,6 +99,12 @@ importers: adm-zip: specifier: ^0.5.12 version: 0.5.12 + ajv: + specifier: ^8.12.0 + version: 8.17.1 + ajv-formats: + specifier: ^3.0.1 + version: 3.0.1(ajv@8.17.1) axios: specifier: ^1.7.7 version: 1.7.7 @@ -210,7 +216,7 @@ importers: version: 6.0.1 ts-jest: specifier: ^29.1.2 - version: 29.1.2(@babel/core@7.24.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.5))(jest@29.7.0(@types/node@20.12.7))(typescript@5.4.5) + version: 29.1.2(@babel/core@7.24.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.5))(jest@29.7.0(@types/node@20.12.7)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.4.5)))(typescript@5.4.5) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@20.12.7)(typescript@5.4.5) @@ -1088,6 +1094,17 @@ packages: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -1634,6 +1651,9 @@ packages: fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fast-uri@3.0.6: + resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -2221,6 +2241,9 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -2646,6 +2669,10 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + require-in-the-middle@7.4.0: resolution: {integrity: sha512-X34iHADNbNDfr6OTStIAHWSAvvKQRYgLO6duASaVf7J2VA3lvmNYboAHOuLC2huav1IwgZJtyEcJCKVzFxOSMQ==} engines: {node: '>=8.6.0'} @@ -4094,6 +4121,17 @@ snapshots: transitivePeerDependencies: - supports-color + ajv-formats@3.0.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.6 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 @@ -4773,6 +4811,8 @@ snapshots: fast-json-stable-stringify@2.1.0: {} + fast-uri@3.0.6: {} + fb-watchman@2.0.2: dependencies: bser: 2.1.1 @@ -5601,6 +5641,8 @@ snapshots: json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@1.0.0: {} + json-schema@0.4.0: {} json5@2.2.3: {} @@ -6004,6 +6046,8 @@ snapshots: require-directory@2.1.1: {} + require-from-string@2.0.2: {} + require-in-the-middle@7.4.0: dependencies: debug: 4.3.7 @@ -6319,7 +6363,7 @@ snapshots: triple-beam@1.4.1: {} - ts-jest@29.1.2(@babel/core@7.24.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.5))(jest@29.7.0(@types/node@20.12.7))(typescript@5.4.5): + ts-jest@29.1.2(@babel/core@7.24.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.5))(jest@29.7.0(@types/node@20.12.7)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.4.5)))(typescript@5.4.5): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 From 60600e638f5f1093ee435932a74c58c89370374a Mon Sep 17 00:00:00 2001 From: thedmail Date: Mon, 27 Jan 2025 15:52:44 -0800 Subject: [PATCH 295/562] Update firebase.md (#1672) docs: Fixes malformed URL --- docs/plugins/firebase.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/firebase.md b/docs/plugins/firebase.md index d0f26b56a..24a52199f 100644 --- a/docs/plugins/firebase.md +++ b/docs/plugins/firebase.md @@ -65,7 +65,7 @@ To provide Firebase credentials, you also need to set up Google Cloud Applicatio Firebase Genkit Monitoring is powered by Google's Cloud operation suite. This requires telemetry related API's to be enabled for your project. Please refer to the [Google Cloud plugin](google-cloud.md#set-up-a-google-cloud-account) documentation for more details. -Grant the following roles to the **"Default compute service account"** within the [Google Cloud IAM Console](https://console.cloud.google.com/iam-admin/iam-admin/iam): +Grant the following roles to the **"Default compute service account"** within the [Google Cloud IAM Console](https://console.cloud.google.com/iam-admin/iam): - **Monitoring Metric Writer** (roles/monitoring.metricWriter) - **Cloud Trace Agent** (roles/cloudtrace.agent) From 7af90f7f64d20368effe9c4e1b6070d34fa36753 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Mon, 27 Jan 2025 18:04:11 -0800 Subject: [PATCH 296/562] refactor(js/ai): moves tool loop logic into its own file, improves readability of generate/action.ts (#1673) --- js/ai/src/generate.ts | 56 +-- js/ai/src/generate/action.ts | 357 ++++++++------------ js/ai/src/generate/resolve-tool-requests.ts | 153 +++++++++ js/ai/src/model.ts | 15 +- js/ai/src/prompt.ts | 4 + js/ai/src/tool.ts | 5 + js/ai/tests/generate/generate_test.ts | 33 +- js/ai/tests/prompt/prompt_test.ts | 12 +- js/genkit/tests/generate_test.ts | 54 ++- js/genkit/tests/prompts_test.ts | 2 +- 10 files changed, 403 insertions(+), 288 deletions(-) create mode 100644 js/ai/src/generate/resolve-tool-requests.ts diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index ab83b5012..f1f84bfb8 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -22,6 +22,7 @@ import { runWithContext, runWithStreamingCallback, sentinelNoopStreamingCallback, + stripUndefinedProps, z, } from '@genkit-ai/core'; import { Channel } from '@genkit-ai/core/async'; @@ -33,7 +34,7 @@ import { resolveFormat, resolveInstructions, } from './formats/index.js'; -import { GenerateUtilParamSchema, generateHelper } from './generate/action.js'; +import { GenerateActionOptions, generateHelper } from './generate/action.js'; import { GenerateResponseChunk } from './generate/chunk.js'; import { GenerateResponse } from './generate/response.js'; import { Message } from './message.js'; @@ -139,11 +140,12 @@ export interface GenerateOptions< context?: ActionContext; } -function applyResumeOption( +/** Amends message history to handle `resume` arguments. Returns the amended history. */ +async function applyResumeOption( options: GenerateOptions, messages: MessageData[] -): MessageData[] { - if (!options.resume) return []; +): Promise { + if (!options.resume) return messages; if ( messages.at(-1)?.role !== 'model' || !messages @@ -159,14 +161,22 @@ function applyResumeOption( const toolRequests = lastModelMessage.content.filter((p) => !!p.toolRequest); const pendingResponses: ToolResponsePart[] = toolRequests - .filter((t) => !!t.metadata?.pendingToolResponse) - .map((t) => ({ - toolResponse: t.metadata!.pendingToolResponse, - })) as ToolResponsePart[]; + .filter((t) => !!t.metadata?.pendingOutput) + .map((t) => + stripUndefinedProps({ + toolResponse: { + name: t.toolRequest!.name, + ref: t.toolRequest!.ref, + output: t.metadata!.pendingOutput, + }, + metadata: { source: 'pending' }, + }) + ) as ToolResponsePart[]; const reply = Array.isArray(options.resume.reply) ? options.resume.reply : [options.resume.reply]; + const message: MessageData = { role: 'tool', content: [...pendingResponses, ...reply], @@ -174,14 +184,14 @@ function applyResumeOption( resume: options.resume.metadata || true, }, }; - return [message]; + return [...messages, message]; } export async function toGenerateRequest( registry: Registry, options: GenerateOptions ): Promise { - const messages: MessageData[] = []; + let messages: MessageData[] = []; if (options.system) { messages.push({ role: 'system', @@ -192,7 +202,7 @@ export async function toGenerateRequest( messages.push(...options.messages.map((m) => Message.parseData(m))); } // resuming from interrupts occurs after message history but before user prompt - messages.push(...applyResumeOption(options, messages)); + messages = await applyResumeOption(options, messages); if (options.prompt) { messages.push({ role: 'user', @@ -346,12 +356,21 @@ export async function generate< jsonSchema: resolvedOptions.output?.jsonSchema, }); + // If is schema is set but format is not explicitly set, default to `json` format. + if (resolvedOptions.output?.schema && !resolvedOptions.output?.format) { + resolvedOptions.output.format = 'json'; + } const resolvedFormat = await resolveFormat(registry, resolvedOptions.output); + const instructions = resolveInstructions( + resolvedFormat, + resolvedSchema, + resolvedOptions?.output?.instructions + ); - const params: z.infer = { + const params: GenerateActionOptions = { model: resolvedModel.modelAction.__action.name, docs: resolvedOptions.docs, - messages: messages, + messages: injectInstructions(messages, instructions), tools, toolChoice: resolvedOptions.toolChoice, config: { @@ -371,15 +390,14 @@ export async function generate< registry, stripNoop(resolvedOptions.onChunk ?? resolvedOptions.streamingCallback), async () => { - const generateFn = () => - generateHelper(registry, { - rawRequest: params, - middleware: resolvedOptions.use, - }); const response = await runWithContext( registry, resolvedOptions.context, - generateFn + () => + generateHelper(registry, { + rawRequest: params, + middleware: resolvedOptions.use, + }) ); const request = await toGenerateRequest(registry, { ...resolvedOptions, diff --git a/js/ai/src/generate/action.ts b/js/ai/src/generate/action.ts index 9701285ce..67d4a3202 100644 --- a/js/ai/src/generate/action.ts +++ b/js/ai/src/generate/action.ts @@ -15,16 +15,15 @@ */ import { - GenkitError, getStreamingCallback, runWithStreamingCallback, + stripUndefinedProps, z, } from '@genkit-ai/core'; import { logger } from '@genkit-ai/core/logging'; import { Registry } from '@genkit-ai/core/registry'; import { toJsonSchema } from '@genkit-ai/core/schema'; import { SPAN_TYPE_ATTR, runInNewSpan } from '@genkit-ai/core/tracing'; -import * as clc from 'colorette'; import { DocumentDataSchema } from '../document.js'; import { injectInstructions, @@ -43,7 +42,6 @@ import { GenerateRequestSchema, GenerateResponseChunkData, GenerateResponseData, - MessageData, MessageSchema, ModelAction, ModelInfo, @@ -52,17 +50,15 @@ import { Part, Role, ToolDefinitionSchema, - ToolResponsePart, resolveModel, } from '../model.js'; +import { ToolAction, resolveTools, toToolDefinition } from '../tool.js'; import { - ToolAction, - ToolInterruptError, - resolveTools, - toToolDefinition, -} from '../tool.js'; + assertValidToolNames, + resolveToolRequests, +} from './resolve-tool-requests.js'; -export const GenerateUtilParamSchema = z.object({ +export const GenerateActionOptionsSchema = z.object({ /** A model name (e.g. `vertexai/gemini-1.0-pro`). */ model: z.string(), /** Retrieved documents to be used as context for this generation. */ @@ -89,6 +85,7 @@ export const GenerateUtilParamSchema = z.object({ /** Maximum number of tool call iterations that can be performed in a single generate call (default 5). */ maxTurns: z.number().optional(), }); +export type GenerateActionOptions = z.infer; /** * Encapsulates all generate logic. This is similar to `generateAction` except not an action and can take middleware. @@ -96,7 +93,7 @@ export const GenerateUtilParamSchema = z.object({ export async function generateHelper( registry: Registry, options: { - rawRequest: z.infer; + rawRequest: GenerateActionOptions; middleware?: ModelMiddleware[]; currentTurn?: number; messageIndex?: number; @@ -130,115 +127,140 @@ export async function generateHelper( ); } -async function generate( +/** Take the raw request and resolve tools, model, and format into their registry action counterparts. */ +async function resolveParameters( registry: Registry, - options: { - rawRequest: z.infer; - middleware: ModelMiddleware[] | undefined; - currentTurn: number; - messageIndex: number; - } -): Promise { - const { modelAction: model } = await resolveModel( - registry, - options.rawRequest.model - ); - if (model.__action.metadata?.model.stage === 'deprecated') { - logger.warn( - `${clc.bold(clc.yellow('Warning:'))} ` + - `Model '${model.__action.name}' is deprecated and may be removed in a future release.` - ); - } - - const tools = await resolveTools(registry, options.rawRequest.tools); - - const resolvedSchema = toJsonSchema({ - jsonSchema: options.rawRequest.output?.jsonSchema, - }); + request: GenerateActionOptions +) { + const [model, tools, format] = await Promise.all([ + resolveModel(registry, request.model, { warnDeprecated: true }).then( + (r) => r.modelAction + ), + resolveTools(registry, request.tools), + resolveFormat(registry, request.output), + ]); + return { model, tools, format }; +} +/** Given a raw request and a formatter, apply the formatter's logic and instructions to the request. */ +function applyFormat( + rawRequest: GenerateActionOptions, + resolvedFormat?: Formatter +) { + const outRequest = { ...rawRequest }; // If is schema is set but format is not explicitly set, default to `json` format. - if ( - options.rawRequest.output?.jsonSchema && - !options.rawRequest.output?.format - ) { - options.rawRequest.output.format = 'json'; + if (rawRequest.output?.jsonSchema && !rawRequest.output?.format) { + outRequest.output = { ...rawRequest.output, format: 'json' }; } - const resolvedFormat = await resolveFormat( - registry, - options.rawRequest.output - ); + const instructions = resolveInstructions( resolvedFormat, - resolvedSchema, - options.rawRequest?.output?.instructions + outRequest.output?.jsonSchema, + outRequest?.output?.instructions ); + if (resolvedFormat) { - options.rawRequest.messages = injectInstructions( - options.rawRequest.messages, - instructions - ); - options.rawRequest.output = { + outRequest.messages = injectInstructions(outRequest.messages, instructions); + outRequest.output = { // use output config from the format ...resolvedFormat.config, // if anything is set explicitly, use that - ...options.rawRequest.output, + ...outRequest.output, }; } - // Create a lookup of tool names with namespaces stripped to original names - const toolMap = tools.reduce>((acc, tool) => { - const name = tool.__action.name; - const shortName = name.substring(name.lastIndexOf('/') + 1); - if (acc[shortName]) { - throw new GenkitError({ - status: 'INVALID_ARGUMENT', - message: `Cannot provide two tools with the same name: '${name}' and '${acc[shortName]}'`, - }); - } - acc[shortName] = tool; - return acc; - }, {}); + return outRequest; +} + +function applyTransferPreamble( + rawRequest: GenerateActionOptions, + transferPreamble?: GenerateActionOptions +): GenerateActionOptions { + if (!transferPreamble) { + return rawRequest; + } + + return stripUndefinedProps({ + ...rawRequest, + messages: [ + ...tagAsPreamble(transferPreamble.messages!)!, + ...rawRequest.messages.filter((m) => !m.metadata?.preamble), + ], + toolChoice: transferPreamble.toolChoice || rawRequest.toolChoice, + tools: transferPreamble.tools || rawRequest.tools, + }); +} + +async function generate( + registry: Registry, + { + rawRequest, + middleware, + currentTurn, + messageIndex, + }: { + rawRequest: GenerateActionOptions; + middleware: ModelMiddleware[] | undefined; + currentTurn: number; + messageIndex: number; + } +): Promise { + const { model, tools, format } = await resolveParameters( + registry, + rawRequest + ); + rawRequest = applyFormat(rawRequest, format); + + // check to make sure we don't have overlapping tool names *before* generation + await assertValidToolNames(tools); const request = await actionToGenerateRequest( - options.rawRequest, + rawRequest, tools, - resolvedFormat, + format, model ); - const accumulatedChunks: GenerateResponseChunkData[] = []; + const previousChunks: GenerateResponseChunkData[] = []; + + let chunkRole: Role = 'model'; + // convenience method to create a full chunk from role and data, append the chunk + // to the previousChunks array, and increment the message index as needed + const makeChunk = ( + role: Role, + chunk: GenerateResponseChunkData + ): GenerateResponseChunk => { + if (role !== chunkRole) messageIndex++; + chunkRole = role; + + const prevToSend = [...previousChunks]; + previousChunks.push(chunk); + + return new GenerateResponseChunk(chunk, { + index: messageIndex, + role, + previousChunks: prevToSend, + parser: format?.handler(request.output?.schema).parseChunk, + }); + }; const streamingCallback = getStreamingCallback(registry); const response = await runWithStreamingCallback( registry, - streamingCallback - ? (chunk: GenerateResponseChunkData) => { - // Store accumulated chunk data - if (streamingCallback) { - streamingCallback!( - new GenerateResponseChunk(chunk, { - index: options.messageIndex, - role: 'model', - previousChunks: accumulatedChunks, - parser: resolvedFormat?.handler(request.output?.schema) - .parseChunk, - }) - ); - } - accumulatedChunks.push(chunk); - } - : undefined, + streamingCallback && + ((chunk: GenerateResponseChunkData) => + streamingCallback(makeChunk('model', chunk))), async () => { const dispatch = async ( index: number, req: z.infer ) => { - if (!options.middleware || index === options.middleware.length) { + if (!middleware || index === middleware.length) { // end of the chain, call the original model action return await model(req); } - const currentMiddleware = options.middleware[index]; + const currentMiddleware = middleware[index]; return currentMiddleware(req, async (modifiedReq) => dispatch(index + 1, modifiedReq || req) ); @@ -246,24 +268,26 @@ async function generate( return new GenerateResponse(await dispatch(0, request), { request, - parser: resolvedFormat?.handler(request.output?.schema).parseMessage, + parser: format?.handler(request.output?.schema).parseMessage, }); } ); // Throw an error if the response is not usable. response.assertValid(); - const message = response.message!; // would have thrown if no message + const generatedMessage = response.message!; // would have thrown if no message - const toolCalls = message.content.filter((part) => !!part.toolRequest); - if (options.rawRequest.returnToolRequests || toolCalls.length === 0) { - if (toolCalls.length === 0) { - response.assertValidSchema(request); - } + const toolRequests = generatedMessage.content.filter( + (part) => !!part.toolRequest + ); + + if (rawRequest.returnToolRequests || toolRequests.length === 0) { + if (toolRequests.length === 0) response.assertValidSchema(request); return response.toJSON(); } - const maxIterations = options.rawRequest.maxTurns ?? 5; - if (options.currentTurn + 1 > maxIterations) { + + const maxIterations = rawRequest.maxTurns ?? 5; + if (currentTurn + 1 > maxIterations) { throw new GenerationResponseError( response, `Exceeded maximum tool call iterations (${maxIterations})`, @@ -272,132 +296,43 @@ async function generate( ); } - const toolResponses: ToolResponsePart[] = []; - let messages: MessageData[] = [...request.messages, message]; - let newTools = options.rawRequest.tools; - let newToolChoice = options.rawRequest.toolChoice; - let interruptedParts: Part[] = []; - let pendingToolRequests: Part[] = []; - for (const part of toolCalls) { - if (!part.toolRequest) { - throw Error( - 'Tool request expected but not provided in tool request part' - ); - } - const tool = toolMap[part.toolRequest?.name]; - if (!tool) { - throw Error(`Tool ${part.toolRequest?.name} not found`); - } - if ((tool.__action.metadata.type as string) === 'prompt') { - try { - const newPreamble = await tool(part.toolRequest?.input); - toolResponses.push({ - toolResponse: { - name: part.toolRequest.name, - ref: part.toolRequest.ref, - output: `transferred to ${part.toolRequest.name}`, - }, - }); - // swap out the preamble - messages = [ - ...tagAsPreamble(newPreamble.messages)!, - ...messages.filter((m) => !m?.metadata?.preamble), - ]; - newTools = newPreamble.tools; - newToolChoice = newPreamble.toolChoice; - } catch (e) { - if (e instanceof ToolInterruptError) { - logger.debug(`interrupted tool ${part.toolRequest?.name}`); - part.metadata = { ...part.metadata, interrupt: e.metadata || true }; - interruptedParts.push(part); - } else { - throw e; - } - } - } else { - try { - const toolOutput = await tool(part.toolRequest?.input); - toolResponses.push({ - toolResponse: { - name: part.toolRequest.name, - ref: part.toolRequest.ref, - output: toolOutput, - }, - }); - // we prep these in case any other tool gets interrupted. - pendingToolRequests.push({ - ...part, - metadata: { - ...part.metadata, - pendingToolResponse: { - name: part.toolRequest.name, - ref: part.toolRequest.ref, - output: toolOutput, - }, - }, - }); - } catch (e) { - if (e instanceof ToolInterruptError) { - logger.debug(`interrupted tool ${part.toolRequest?.name}`); - part.metadata = { ...part.metadata, interrupt: e.metadata || true }; - interruptedParts.push(part); - } else { - throw e; - } - } - } - } - options.messageIndex++; - const nextRequest = { - ...options.rawRequest, - messages: [ - ...messages, - { - role: 'tool', - content: toolResponses, - }, - ] as MessageData[], - tools: newTools, - toolCoice: newToolChoice, - }; - // stream out the tool responses - streamingCallback?.( - new GenerateResponseChunk( - { - content: toolResponses, - }, - { - index: options.messageIndex, - role: 'model', - previousChunks: accumulatedChunks, - parser: resolvedFormat?.handler(request.output?.schema).parseChunk, - } - ) - ); - if (interruptedParts.length > 0) { - const nonToolParts = - (response.message?.content.filter((c) => !c.toolRequest) as Part[]) || []; + const { revisedModelMessage, toolMessage, transferPreamble } = + await resolveToolRequests(registry, rawRequest, generatedMessage); + + // if an interrupt message is returned, stop the tool loop and return a response + if (revisedModelMessage) { return { ...response.toJSON(), finishReason: 'interrupted', - message: { - role: 'model', - content: nonToolParts - .concat(pendingToolRequests) - .concat(interruptedParts), - }, + finishMessage: 'One or more tool calls resulted in interrupts.', + message: revisedModelMessage, }; } + + // if the loop will continue, stream out the tool response message... + streamingCallback?.( + makeChunk('tool', { + content: toolMessage!.content, + }) + ); + + let nextRequest = { + ...rawRequest, + messages: [...rawRequest.messages, generatedMessage, toolMessage!], + }; + nextRequest = applyTransferPreamble(nextRequest, transferPreamble); + + // then recursively call for another loop return await generateHelper(registry, { rawRequest: nextRequest, - middleware: options.middleware, - currentTurn: options.currentTurn + 1, - messageIndex: options.messageIndex + 1, + middleware: middleware, + currentTurn: currentTurn + 1, + messageIndex: messageIndex + 1, }); } async function actionToGenerateRequest( - options: z.infer, + options: GenerateActionOptions, resolvedTools: ToolAction[] | undefined, resolvedFormat: Formatter | undefined, model: ModelAction diff --git a/js/ai/src/generate/resolve-tool-requests.ts b/js/ai/src/generate/resolve-tool-requests.ts new file mode 100644 index 000000000..7376fad50 --- /dev/null +++ b/js/ai/src/generate/resolve-tool-requests.ts @@ -0,0 +1,153 @@ +/** + * 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. + */ + +import { GenkitError, stripUndefinedProps } from '@genkit-ai/core'; +import { logger } from '@genkit-ai/core/logging'; +import { Registry } from '@genkit-ai/core/registry'; +import { MessageData, ToolResponsePart } from '../model.js'; +import { isPromptAction } from '../prompt.js'; +import { ToolAction, ToolInterruptError, resolveTools } from '../tool.js'; +import { GenerateActionOptions } from './action.js'; + +export function toToolMap(tools: ToolAction[]): Record { + assertValidToolNames(tools); + const out: Record = {}; + for (const tool of tools) { + const name = tool.__action.name; + const shortName = name.substring(name.lastIndexOf('/') + 1); + out[shortName] = tool; + } + return out; +} + +/** Ensures that each tool has a unique name. */ +export function assertValidToolNames(tools: ToolAction[]) { + const nameMap: Record = {}; + for (const tool of tools) { + const name = tool.__action.name; + const shortName = name.substring(name.lastIndexOf('/') + 1); + if (nameMap[shortName]) { + throw new GenkitError({ + status: 'INVALID_ARGUMENT', + message: `Cannot provide two tools with the same name: '${name}' and '${nameMap[shortName]}'`, + }); + } + nameMap[shortName] = name; + } +} + +/** + * resolveToolRequests is responsible for executing the tools requested by the model for a single turn. it + * returns either a toolMessage to append or a revisedModelMessage when an interrupt occurs, and a transferPreamble + * if a prompt tool is called + */ +export async function resolveToolRequests( + registry: Registry, + rawRequest: GenerateActionOptions, + generatedMessage: MessageData +): Promise<{ + revisedModelMessage?: MessageData; + toolMessage?: MessageData; + transferPreamble?: GenerateActionOptions; +}> { + const toolMap = toToolMap(await resolveTools(registry, rawRequest.tools)); + + const responseParts: ToolResponsePart[] = []; + let hasInterrupts: boolean = false; + let transferPreamble: GenerateActionOptions | undefined; + + const revisedModelMessage = { + ...generatedMessage, + content: [...generatedMessage.content], + }; + + await Promise.all( + revisedModelMessage.content.map(async (part, i) => { + if (!part.toolRequest) return; // skip non-tool-request parts + + const tool = toolMap[part.toolRequest.name]; + if (!tool) { + throw new GenkitError({ + status: 'NOT_FOUND', + message: `Tool ${part.toolRequest.name} not found`, + detail: { request: rawRequest }, + }); + } + + // if it's a prompt action, go ahead and render the preamble + if (isPromptAction(tool)) { + if (transferPreamble) + throw new GenkitError({ + status: 'INVALID_ARGUMENT', + message: `Model attempted to transfer to multiple prompt tools.`, + }); + transferPreamble = await tool(part.toolRequest.input); + responseParts.push({ + toolResponse: { + name: part.toolRequest.name, + ref: part.toolRequest.ref, + output: `transferred to ${part.toolRequest.name}`, + }, + }); + return; + } + + // otherwise, execute the tool and catch interrupts + try { + const output = await tool(part.toolRequest.input, {}); + const responsePart = stripUndefinedProps({ + toolResponse: { + name: part.toolRequest.name, + ref: part.toolRequest.ref, + output, + }, + }); + + revisedModelMessage.content.splice(i, 1, { + ...part, + metadata: { + ...part.metadata, + pendingOutput: responsePart.toolResponse.output, + }, + }); + responseParts.push(responsePart); + } catch (e) { + if (e instanceof ToolInterruptError) { + logger.debug( + `tool '${toolMap[part.toolRequest?.name].__action.name}' triggered an interrupt${e.metadata ? `: ${JSON.stringify(e.metadata)}` : ''}` + ); + revisedModelMessage.content.splice(i, 1, { + toolRequest: part.toolRequest, + metadata: { ...part.metadata, interrupt: e.metadata || true }, + }); + hasInterrupts = true; + return; + } + + throw e; + } + }) + ); + + if (hasInterrupts) { + return { revisedModelMessage }; + } + + return { + toolMessage: { role: 'tool', content: responseParts }, + transferPreamble, + }; +} diff --git a/js/ai/src/model.ts b/js/ai/src/model.ts index ce15adaa3..f23e90206 100644 --- a/js/ai/src/model.ts +++ b/js/ai/src/model.ts @@ -23,6 +23,7 @@ import { StreamingCallback, z, } from '@genkit-ai/core'; +import { logger } from '@genkit-ai/core/logging'; import { Registry } from '@genkit-ai/core/registry'; import { toJsonSchema } from '@genkit-ai/core/schema'; import { performance } from 'node:perf_hooks'; @@ -633,7 +634,8 @@ export interface ResolvedModel< export async function resolveModel( registry: Registry, - model: ModelArgument | undefined + model: ModelArgument | undefined, + options?: { warnDeprecated?: boolean } ): Promise> { let out: ResolvedModel; let modelId: string; @@ -670,9 +672,18 @@ export async function resolveModel( if (!out.modelAction) { throw new GenkitError({ status: 'NOT_FOUND', - message: `Model ${modelId} not found`, + message: `Model '${modelId}' not found`, }); } + if ( + options?.warnDeprecated && + out.modelAction.__action.metadata?.model?.stage === 'deprecated' + ) { + logger.warn( + `Model '${out.modelAction.__action.name}' is deprecated and may be removed in a future release.` + ); + } + return out; } diff --git a/js/ai/src/prompt.ts b/js/ai/src/prompt.ts index abfe82e6d..c649198f3 100644 --- a/js/ai/src/prompt.ts +++ b/js/ai/src/prompt.ts @@ -74,6 +74,10 @@ export type PromptAction = Action< __executablePrompt: ExecutablePrompt; }; +export function isPromptAction(action: Action): action is PromptAction { + return action.__action.metadata?.type === 'prompt'; +} + /** * Prompt action. */ diff --git a/js/ai/src/tool.ts b/js/ai/src/tool.ts index da9133560..a39cc96da 100644 --- a/js/ai/src/tool.ts +++ b/js/ai/src/tool.ts @@ -46,7 +46,12 @@ export type ToolAction< * it exists. */ reply( + /** The interrupt tool request to which you want to respond. */ interrupt: ToolRequestPart, + /** + * The data with which you want to respond. Must conform to a tool's output schema or an + * interrupt's input schema. + **/ replyData: z.infer, options?: { metadata?: Record } ): ToolResponsePart; diff --git a/js/ai/tests/generate/generate_test.ts b/js/ai/tests/generate/generate_test.ts index 872c40007..ac5399a83 100644 --- a/js/ai/tests/generate/generate_test.ts +++ b/js/ai/tests/generate/generate_test.ts @@ -290,17 +290,13 @@ describe('toGenerateRequest', () => { { toolRequest: { name: 'p1', ref: '1', input: { one: '1' } }, metadata: { - pendingToolResponse: { name: 'p1', ref: '1', output: 'done' }, + pendingOutput: 'done', }, }, { toolRequest: { name: 'p2', ref: '2', input: { one: '1' } }, metadata: { - pendingToolResponse: { - name: 'p2', - ref: '2', - output: 'done2', - }, + pendingOutput: 'done2', }, }, { @@ -338,17 +334,13 @@ describe('toGenerateRequest', () => { { toolRequest: { name: 'p1', ref: '1', input: { one: '1' } }, metadata: { - pendingToolResponse: { name: 'p1', ref: '1', output: 'done' }, + pendingOutput: 'done', }, }, { toolRequest: { name: 'p2', ref: '2', input: { one: '1' } }, metadata: { - pendingToolResponse: { - name: 'p2', - ref: '2', - output: 'done2', - }, + pendingOutput: 'done2', }, }, { @@ -371,8 +363,14 @@ describe('toGenerateRequest', () => { resume: true, }, content: [ - { toolResponse: { name: 'p1', ref: '1', output: 'done' } }, - { toolResponse: { name: 'p2', ref: '2', output: 'done2' } }, + { + toolResponse: { name: 'p1', ref: '1', output: 'done' }, + metadata: { source: 'pending' }, + }, + { + toolResponse: { name: 'p2', ref: '2', output: 'done2' }, + metadata: { source: 'pending' }, + }, { toolResponse: { name: 'i1', ref: '3', output: 'done3' } }, { toolResponse: { name: 'i2', ref: '4', output: 'done4' } }, ], @@ -391,10 +389,11 @@ describe('toGenerateRequest', () => { { name: 'GenkitError', status: test.throws } ); } else { - assert.deepStrictEqual( - await toGenerateRequest(registry, test.prompt as GenerateOptions), - test.expectedOutput + const actualOutput = await toGenerateRequest( + registry, + test.prompt as GenerateOptions ); + assert.deepStrictEqual(actualOutput, test.expectedOutput); } }); } diff --git a/js/ai/tests/prompt/prompt_test.ts b/js/ai/tests/prompt/prompt_test.ts index f2db66e3a..214f454be 100644 --- a/js/ai/tests/prompt/prompt_test.ts +++ b/js/ai/tests/prompt/prompt_test.ts @@ -16,19 +16,19 @@ import { ActionContext, runWithContext, z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; +import { toJsonSchema } from '@genkit-ai/core/schema'; import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; -import { toJsonSchema } from '../../../core/src/schema'; -import { Document } from '../../lib/document'; -import { GenerateOptions } from '../../lib/index'; -import { Session } from '../../lib/session'; +import { Document } from '../../src/document.js'; +import { GenerateOptions } from '../../src/index.js'; import { ModelAction, defineModel } from '../../src/model'; import { PromptConfig, PromptGenerateOptions, definePrompt, -} from '../../src/prompt'; -import { defineTool } from '../../src/tool'; +} from '../../src/prompt.js'; +import { Session } from '../../src/session.js'; +import { defineTool } from '../../src/tool.js'; describe('prompt', () => { let registry; diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index c6337d6ab..81285862d 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -218,9 +218,7 @@ describe('generate', () => { // nothing } }, - (e: Error) => { - return e.message.includes('Model modelNotFound not found'); - } + { status: 'NOT_FOUND' } ); }); @@ -589,7 +587,7 @@ describe('generate', () => { }, ], index: 1, - role: 'model', + role: 'tool', }, { content: [{ text: 'done' }], @@ -688,13 +686,18 @@ describe('generate', () => { assert.strictEqual(reqCounter, 1); assert.deepStrictEqual(response.toolRequests, [ { + toolRequest: { + input: {}, + name: 'interruptingTool', + ref: 'ref123', + }, metadata: { - pendingToolResponse: { - name: 'simpleTool', - output: 'response: foo', - ref: 'ref123', + interrupt: { + confirm: 'is it a banana?', }, }, + }, + { toolRequest: { input: { name: 'foo', @@ -702,17 +705,8 @@ describe('generate', () => { name: 'simpleTool', ref: 'ref123', }, - }, - { - toolRequest: { - input: {}, - name: 'interruptingTool', - ref: 'ref123', - }, metadata: { - interrupt: { - confirm: 'is it a banana?', - }, + pendingOutput: 'response: foo', }, }, ]); @@ -724,12 +718,17 @@ describe('generate', () => { }, { metadata: { - pendingToolResponse: { - name: 'simpleTool', - output: 'response: foo', - ref: 'ref123', + interrupt: { + confirm: 'is it a banana?', }, }, + toolRequest: { + input: {}, + name: 'interruptingTool', + ref: 'ref123', + }, + }, + { toolRequest: { input: { name: 'foo', @@ -737,17 +736,8 @@ describe('generate', () => { name: 'simpleTool', ref: 'ref123', }, - }, - { metadata: { - interrupt: { - confirm: 'is it a banana?', - }, - }, - toolRequest: { - input: {}, - name: 'interruptingTool', - ref: 'ref123', + pendingOutput: 'response: foo', }, }, ], diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index 13622cda6..6a19d7f99 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -601,7 +601,7 @@ describe('definePrompt - dotprompt', () => { const response = hi({ name: 'Genkit' }); await assert.rejects(response, { - message: 'NOT_FOUND: Model modelThatDoesNotExist not found', + message: "NOT_FOUND: Model 'modelThatDoesNotExist' not found", }); }); }); From 0272f277a8d5338b10d7c379fb24df335439f777 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 27 Jan 2025 21:55:20 -0500 Subject: [PATCH 297/562] feat(js/plugins/vertexai): instruduced gemini model ref helper and ability to register versions (#1668) Co-authored-by: Michael Doyle --- js/plugins/vertexai/src/common/index.ts | 10 ++ js/plugins/vertexai/src/common/types.ts | 9 +- js/plugins/vertexai/src/gemini.ts | 130 ++++++++++++++-- js/plugins/vertexai/src/index.ts | 30 +++- .../vertexai/src/modelgarden/mistral.ts | 2 + js/plugins/vertexai/tests/plugin_test.ts | 143 ++++++++++++++++++ 6 files changed, 307 insertions(+), 17 deletions(-) create mode 100644 js/plugins/vertexai/tests/plugin_test.ts diff --git a/js/plugins/vertexai/src/common/index.ts b/js/plugins/vertexai/src/common/index.ts index 77e098be0..56f472578 100644 --- a/js/plugins/vertexai/src/common/index.ts +++ b/js/plugins/vertexai/src/common/index.ts @@ -40,9 +40,19 @@ function parseFirebaseProjectId(): string | undefined { } } +/** @hidden */ +export function __setFakeDerivedParams(params: any) { + __fake_getDerivedParams = params; +} +let __fake_getDerivedParams: any; + export async function getDerivedParams( options?: PluginOptions ): Promise { + if (__fake_getDerivedParams) { + return __fake_getDerivedParams; + } + let authOptions = options?.googleAuth; let authClient: GoogleAuth; const providedProjectId = diff --git a/js/plugins/vertexai/src/common/types.ts b/js/plugins/vertexai/src/common/types.ts index a88fef9e6..642f5980a 100644 --- a/js/plugins/vertexai/src/common/types.ts +++ b/js/plugins/vertexai/src/common/types.ts @@ -14,7 +14,9 @@ * limitations under the License. */ +import { ModelReference } from 'genkit'; import { GoogleAuthOptions } from 'google-auth-library'; +import { GeminiConfigSchema } from '../gemini'; /** Common options for Vertex AI plugin configuration */ export interface CommonPluginOptions { @@ -27,4 +29,9 @@ export interface CommonPluginOptions { } /** Combined plugin options, extending common options with subplugin-specific options */ -export interface PluginOptions extends CommonPluginOptions {} +export interface PluginOptions extends CommonPluginOptions { + models?: ( + | ModelReference + | string + )[]; +} diff --git a/js/plugins/vertexai/src/gemini.ts b/js/plugins/vertexai/src/gemini.ts index 9281173b4..60b5abc36 100644 --- a/js/plugins/vertexai/src/gemini.ts +++ b/js/plugins/vertexai/src/gemini.ts @@ -39,6 +39,7 @@ import { MediaPart, MessageData, ModelAction, + ModelInfo, ModelMiddleware, ModelReference, Part, @@ -166,6 +167,69 @@ export const GeminiConfigSchema = GenerationCommonConfigSchema.extend({ .optional(), }); +/** + * Known model names, to allow code completion for convenience. Allows other model names. + */ +export type GeminiVersionString = + | keyof typeof SUPPORTED_GEMINI_MODELS + | (string & {}); + +/** + * Returns a reference to a model that can be used in generate calls. + * + * ```js + * await ai.generate({ + * prompt: 'hi', + * model: gemini('gemini-1.5-flash') + * }); + * ``` + */ +export function gemini( + version: GeminiVersionString, + options: GeminiConfig = {} +): ModelReference { + const nearestModel = nearestGeminiModelRef(version); + return modelRef({ + name: `vertexai/${version}`, + config: options, + configSchema: GeminiConfigSchema, + info: { + ...nearestModel.info, + // If exact suffix match for a known model, use its label, otherwise create a new label + label: nearestModel.name.endsWith(version) + ? nearestModel.info?.label + : `Vertex AI - ${version}`, + }, + }); +} + +function nearestGeminiModelRef( + version: GeminiVersionString, + options: GeminiConfig = {} +): ModelReference { + const matchingKey = longestMatchingPrefix( + version, + Object.keys(SUPPORTED_GEMINI_MODELS) + ); + if (matchingKey) { + return SUPPORTED_GEMINI_MODELS[matchingKey].withConfig({ + ...options, + version, + }); + } + return GENERIC_GEMINI_MODEL.withConfig({ ...options, version }); +} + +function longestMatchingPrefix(version: string, potentialMatches: string[]) { + return potentialMatches + .filter((p) => version.startsWith(p)) + .reduce( + (longest, current) => + current.length > longest.length ? current : longest, + '' + ); +} + /** * Gemini model configuration options. * @@ -268,6 +332,21 @@ export const gemini20FlashExp = modelRef({ configSchema: GeminiConfigSchema, }); +export const GENERIC_GEMINI_MODEL = modelRef({ + name: 'vertexai/gemini', + configSchema: GeminiConfigSchema, + info: { + label: 'Google Gemini', + supports: { + multiturn: true, + media: true, + tools: true, + toolChoice: true, + systemRole: true, + }, + }, +}); + export const SUPPORTED_V1_MODELS = { 'gemini-1.0-pro': gemini10Pro, }; @@ -281,11 +360,11 @@ export const SUPPORTED_V15_MODELS = { export const SUPPORTED_GEMINI_MODELS = { ...SUPPORTED_V1_MODELS, ...SUPPORTED_V15_MODELS, -}; +} as const; function toGeminiRole( role: MessageData['role'], - model?: ModelReference + modelInfo?: ModelInfo ): string { switch (role) { case 'user': @@ -293,7 +372,7 @@ function toGeminiRole( case 'model': return 'model'; case 'system': - if (model && SUPPORTED_V15_MODELS[model.name]) { + if (modelInfo && modelInfo.supports?.systemRole) { // We should have already pulled out the supported system messages, // anything remaining is unsupported; throw an error. throw new Error( @@ -387,10 +466,10 @@ export function toGeminiSystemInstruction(message: MessageData): Content { export function toGeminiMessage( message: MessageData, - model?: ModelReference + modelInfo?: ModelInfo ): Content { return { - role: toGeminiRole(message.role, model), + role: toGeminiRole(message.role, modelInfo), parts: message.content.map(toGeminiPart), }; } @@ -581,7 +660,7 @@ export function cleanSchema(schema: JSONSchema): JSONSchema { /** * Define a Vertex AI Gemini model. */ -export function defineGeminiModel( +export function defineGeminiKnownModel( ai: Genkit, name: string, vertexClientFactory: ( @@ -594,11 +673,34 @@ export function defineGeminiModel( const model: ModelReference = SUPPORTED_GEMINI_MODELS[name]; if (!model) throw new Error(`Unsupported model: ${name}`); + return defineGeminiModel( + ai, + modelName, + name, + model?.info, + vertexClientFactory, + options + ); +} + +/** + * Define a Vertex AI Gemini model. + */ +export function defineGeminiModel( + ai: Genkit, + modelName: string, + version: string, + modelInfo: ModelInfo | undefined, + vertexClientFactory: ( + request: GenerateRequest + ) => VertexAI, + options: PluginOptions +): ModelAction { const middlewares: ModelMiddleware[] = []; - if (SUPPORTED_V1_MODELS[name]) { + if (SUPPORTED_V1_MODELS[version]) { middlewares.push(simulateSystemPrompt()); } - if (model?.info?.supports?.media) { + if (modelInfo?.supports?.media) { // the gemini api doesn't support downloading media from http(s) middlewares.push(downloadRequestMedia({ maxBytes: 1024 * 1024 * 20 })); } @@ -606,7 +708,7 @@ export function defineGeminiModel( return ai.defineModel( { name: modelName, - ...model.info, + ...modelInfo, configSchema: GeminiConfigSchema, use: middlewares, }, @@ -619,7 +721,7 @@ export function defineGeminiModel( // Handle system instructions separately let systemInstruction: Content | undefined = undefined; - if (SUPPORTED_V15_MODELS[name]) { + if (!SUPPORTED_V1_MODELS[version]) { const systemMessage = messages.find((m) => m.role === 'system'); if (systemMessage) { messages.splice(messages.indexOf(systemMessage), 1); @@ -659,7 +761,7 @@ export function defineGeminiModel( toolConfig, history: messages .slice(0, -1) - .map((message) => toGeminiMessage(message, model)), + .map((message) => toGeminiMessage(message, modelInfo)), generationConfig: { candidateCount: request.candidates || undefined, temperature: request.config?.temperature, @@ -673,9 +775,7 @@ export function defineGeminiModel( }; // Handle cache - const modelVersion = (request.config?.version || - model.version || - name) as string; + const modelVersion = (request.config?.version || version) as string; const cacheConfigDetails = extractCacheConfig(request); const apiClient = new ApiClient( @@ -727,7 +827,7 @@ export function defineGeminiModel( }); } - const msg = toGeminiMessage(messages[messages.length - 1], model); + const msg = toGeminiMessage(messages[messages.length - 1], modelInfo); if (cache) { genModel = vertex.preview.getGenerativeModelFromCachedContent( diff --git a/js/plugins/vertexai/src/index.ts b/js/plugins/vertexai/src/index.ts index 18114488a..47de7c9a0 100644 --- a/js/plugins/vertexai/src/index.ts +++ b/js/plugins/vertexai/src/index.ts @@ -35,7 +35,9 @@ import { } from './embedder.js'; import { SUPPORTED_GEMINI_MODELS, + defineGeminiKnownModel, defineGeminiModel, + gemini, gemini10Pro, gemini15Flash, gemini15Pro, @@ -51,6 +53,7 @@ import { } from './imagen.js'; export { type PluginOptions } from './common/types.js'; export { + gemini, gemini10Pro, gemini15Flash, gemini15Pro, @@ -78,8 +81,33 @@ export function vertexAI(options?: PluginOptions): GenkitPlugin { imagenModel(ai, name, authClient, { projectId, location }) ); Object.keys(SUPPORTED_GEMINI_MODELS).map((name) => - defineGeminiModel(ai, name, vertexClientFactory, { projectId, location }) + defineGeminiKnownModel(ai, name, vertexClientFactory, { + projectId, + location, + }) ); + if (options?.models) { + for (const modelOrRef of options?.models) { + const modelName = + typeof modelOrRef === 'string' + ? modelOrRef + : // strip out the `vertexai/` prefix + modelOrRef.name.split('/')[1]; + const modelRef = + typeof modelOrRef === 'string' ? gemini(modelOrRef) : modelOrRef; + defineGeminiModel( + ai, + modelRef.name, + modelName, + modelRef.info, + vertexClientFactory, + { + projectId, + location, + } + ); + } + } Object.keys(SUPPORTED_EMBEDDER_MODELS).map((name) => defineVertexAIEmbedder(ai, name, authClient, { projectId, location }) diff --git a/js/plugins/vertexai/src/modelgarden/mistral.ts b/js/plugins/vertexai/src/modelgarden/mistral.ts index 05c04f8a4..b2aac4dde 100644 --- a/js/plugins/vertexai/src/modelgarden/mistral.ts +++ b/js/plugins/vertexai/src/modelgarden/mistral.ts @@ -124,6 +124,8 @@ function toMistralRole(role: Role): MistralRole { return 'tool'; case 'system': return 'system'; + default: + throw new Error(`Unknwon role ${role}`); } } function toMistralToolRequest(toolRequest: Record): FunctionCall { diff --git a/js/plugins/vertexai/tests/plugin_test.ts b/js/plugins/vertexai/tests/plugin_test.ts new file mode 100644 index 000000000..9fc731b70 --- /dev/null +++ b/js/plugins/vertexai/tests/plugin_test.ts @@ -0,0 +1,143 @@ +/** + * Copyright 2024 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 * as assert from 'assert'; +import { genkit } from 'genkit'; +import { ModelInfo } from 'genkit/model'; +import { describe, it } from 'node:test'; +import { __setFakeDerivedParams } from '../src/common/index.js'; +import { GENERIC_GEMINI_MODEL, gemini } from '../src/gemini.js'; +import vertexAI, { gemini15Flash, gemini15Pro } from '../src/index.js'; + +describe('plugin', () => { + __setFakeDerivedParams({ + projectId: 'test', + location: 'us-central1', + }); + + it('should init the plugin without requiring the api key', async () => { + const ai = genkit({ + plugins: [vertexAI()], + }); + + assert.ok(ai); + }); + + it('should pre-register a few flagship models', async () => { + const ai = genkit({ + plugins: [vertexAI()], + }); + + assert.ok(await ai.registry.lookupAction(`/model/${gemini15Flash.name}`)); + assert.ok(await ai.registry.lookupAction(`/model/${gemini15Pro.name}`)); + }); + + it('allow referencing models using `gemini` helper', async () => { + const ai = genkit({ + plugins: [vertexAI()], + }); + + const pro = await ai.registry.lookupAction( + `/model/${gemini('gemini-1.5-pro').name}` + ); + assert.ok(pro); + assert.strictEqual(pro.__action.name, 'vertexai/gemini-1.5-pro'); + const flash = await ai.registry.lookupAction( + `/model/${gemini('gemini-1.5-flash').name}` + ); + assert.ok(flash); + assert.strictEqual(flash.__action.name, 'vertexai/gemini-1.5-flash'); + }); + + it('references explicitly registered models', async () => { + const flash002Ref = gemini('gemini-1.5-flash-002'); + const ai = genkit({ + plugins: [ + vertexAI({ + location: 'us-central1', + models: ['gemini-1.5-pro-002', flash002Ref, 'gemini-4.0-banana'], + }), + ], + }); + + const pro002Ref = gemini('gemini-1.5-pro-002'); + assert.strictEqual(pro002Ref.name, 'vertexai/gemini-1.5-pro-002'); + assertEqualModelInfo( + pro002Ref.info!, + 'Vertex AI - gemini-1.5-pro-002', + gemini15Pro.info! + ); + const pro002 = await ai.registry.lookupAction(`/model/${pro002Ref.name}`); + assert.ok(pro002); + assert.strictEqual(pro002.__action.name, 'vertexai/gemini-1.5-pro-002'); + assertEqualModelInfo( + pro002.__action.metadata?.model, + 'Vertex AI - gemini-1.5-pro-002', + gemini15Pro.info! + ); + + assert.strictEqual(flash002Ref.name, 'vertexai/gemini-1.5-flash-002'); + assertEqualModelInfo( + flash002Ref.info!, + 'Vertex AI - gemini-1.5-flash-002', + gemini15Flash.info! + ); + const flash002 = await ai.registry.lookupAction( + `/model/${flash002Ref.name}` + ); + assert.ok(flash002); + assert.strictEqual(flash002.__action.name, 'vertexai/gemini-1.5-flash-002'); + assertEqualModelInfo( + flash002.__action.metadata?.model, + 'Vertex AI - gemini-1.5-flash-002', + gemini15Flash.info! + ); + + const bananaRef = gemini('gemini-4.0-banana'); + assert.strictEqual(bananaRef.name, 'vertexai/gemini-4.0-banana'); + assertEqualModelInfo( + bananaRef.info!, + 'Vertex AI - gemini-4.0-banana', + GENERIC_GEMINI_MODEL.info! // <---- generic model fallback + ); + const banana = await ai.registry.lookupAction(`/model/${bananaRef.name}`); + assert.ok(banana); + assert.strictEqual(banana.__action.name, 'vertexai/gemini-4.0-banana'); + assertEqualModelInfo( + banana.__action.metadata?.model, + 'Vertex AI - gemini-4.0-banana', + GENERIC_GEMINI_MODEL.info! // <---- generic model fallback + ); + + // this one is not registered + const flash003Ref = gemini('gemini-1.5-flash-003'); + assert.strictEqual(flash003Ref.name, 'vertexai/gemini-1.5-flash-003'); + const flash003 = await ai.registry.lookupAction( + `/model/${flash003Ref.name}` + ); + assert.ok(flash003 === undefined); + }); +}); + +function assertEqualModelInfo( + modelAction: ModelInfo, + expectedLabel: string, + expectedInfo: ModelInfo +) { + assert.strictEqual(modelAction.label, expectedLabel); + assert.deepStrictEqual(modelAction.supports, expectedInfo.supports); + assert.deepStrictEqual(modelAction.versions, expectedInfo.versions); +} From 300f41b4476cebde0c28bac40091e271203b1fe4 Mon Sep 17 00:00:00 2001 From: Michael Doyle Date: Tue, 28 Jan 2025 12:26:53 -0500 Subject: [PATCH 298/562] fix(js/plugins/google-cloud)!: rename io to inputAndOutput (#1674) BREAKING CHANGE: IO generally refers to interfaces between a computer and the outside world. Reading/writing from disk, reading/writing from the network, etc (and also of course peripherals like mice, keyboards, monitors, etc). Renaming this removes ambiguity that we are referring to something different. --- docs/plugins/google-cloud.md | 2 +- .../google-cloud/src/gcpOpenTelemetry.ts | 34 +++++++++++++++---- .../google-cloud/src/telemetry/action.ts | 14 ++++---- .../google-cloud/src/telemetry/defaults.ts | 5 ++- .../google-cloud/src/telemetry/engagement.ts | 2 +- .../google-cloud/src/telemetry/feature.ts | 14 ++++---- .../google-cloud/src/telemetry/generate.ts | 6 ++-- js/plugins/google-cloud/src/telemetry/path.ts | 2 +- js/plugins/google-cloud/src/types.ts | 4 +-- ...o_test.ts => logs_no_input_output_test.ts} | 2 +- 10 files changed, 52 insertions(+), 33 deletions(-) rename js/plugins/google-cloud/tests/{logs_no_io_test.ts => logs_no_input_output_test.ts} (99%) diff --git a/docs/plugins/google-cloud.md b/docs/plugins/google-cloud.md index 7f2b9fd50..10c7eced6 100644 --- a/docs/plugins/google-cloud.md +++ b/docs/plugins/google-cloud.md @@ -197,7 +197,7 @@ and logs. Provides an override that disables exporting traces while still exprting metrics and logs. -#### disableLoggingIO +#### disableLoggingInputAndOutput Provides an override that disables collecting input and output logs. diff --git a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts index cada54632..6d7217625 100644 --- a/js/plugins/google-cloud/src/gcpOpenTelemetry.ts +++ b/js/plugins/google-cloud/src/gcpOpenTelemetry.ts @@ -128,7 +128,7 @@ export class GcpOpenTelemetry { credentials: this.config.credentials, }) : new InMemorySpanExporter(), - this.config.exportIO, + this.config.exportInputAndOutput, this.config.projectId, getErrorHandler( (err) => { @@ -245,7 +245,7 @@ class MetricExporterWrapper extends MetricExporter { class AdjustingTraceExporter implements SpanExporter { constructor( private exporter: SpanExporter, - private logIO: boolean, + private logInputAndOutput: boolean, private projectId?: string, private errorHandler?: (error: Error) => void ) {} @@ -354,26 +354,46 @@ class AdjustingTraceExporter implements SpanExporter { if (isRoot) { // Report top level feature request and latency only for root spans // Log input to and output from to the feature - featuresTelemetry.tick(span, unused, this.logIO, this.projectId); + featuresTelemetry.tick( + span, + unused, + this.logInputAndOutput, + this.projectId + ); // Report executions and latency for all flow paths only on the root span - pathsTelemetry.tick(span, paths, this.logIO, this.projectId); + pathsTelemetry.tick(span, paths, this.logInputAndOutput, this.projectId); // Set root status explicitly span.attributes['genkit:rootState'] = span.attributes['genkit:state']; } if (type === 'action' && subtype === 'model') { // Report generate metrics () for all model actions - generateTelemetry.tick(span, unused, this.logIO, this.projectId); + generateTelemetry.tick( + span, + unused, + this.logInputAndOutput, + this.projectId + ); } if (type === 'action' && subtype === 'tool') { // TODO: Report input and output for tool actions } if (type === 'action' || type === 'flow' || type == 'flowStep') { // Report request and latency metrics for all actions - actionTelemetry.tick(span, unused, this.logIO, this.projectId); + actionTelemetry.tick( + span, + unused, + this.logInputAndOutput, + this.projectId + ); } if (type === 'userEngagement') { // Report user acceptance and feedback metrics - engagementTelemetry.tick(span, unused, this.logIO, this.projectId); + engagementTelemetry.tick( + span, + unused, + this.logInputAndOutput, + this.projectId + ); } } diff --git a/js/plugins/google-cloud/src/telemetry/action.ts b/js/plugins/google-cloud/src/telemetry/action.ts index adb75251b..e3630bb1d 100644 --- a/js/plugins/google-cloud/src/telemetry/action.ts +++ b/js/plugins/google-cloud/src/telemetry/action.ts @@ -52,7 +52,7 @@ class ActionTelemetry implements Telemetry { tick( span: ReadableSpan, paths: Set, - logIO: boolean, + logInputAndOutput: boolean, projectId?: string ): void { const attributes = span.attributes; @@ -78,14 +78,14 @@ class ActionTelemetry implements Telemetry { logger.warn(`Unknown action state; ${state}`); } - if (subtype === 'tool' && logIO) { + if (subtype === 'tool' && logInputAndOutput) { const input = attributes['genkit:input'] as string; const output = attributes['genkit:output'] as string; const sessionId = attributes['genkit:sessionId'] as string; const threadName = attributes['genkit:threadName'] as string; if (input) { - this.recordIO( + this.writeLog( span, 'Input', featureName, @@ -97,7 +97,7 @@ class ActionTelemetry implements Telemetry { ); } if (output) { - this.recordIO( + this.writeLog( span, 'Output', featureName, @@ -149,12 +149,12 @@ class ActionTelemetry implements Telemetry { this.actionLatencies.record(latencyMs, dimensions); } - private recordIO( + private writeLog( span: ReadableSpan, tag: string, featureName: string, qualifiedPath: string, - input: string, + content: string, projectId?: string, sessionId?: string, threadName?: string @@ -170,7 +170,7 @@ class ActionTelemetry implements Telemetry { }; logger.logStructured(`${tag}[${path}, ${featureName}]`, { ...sharedMetadata, - content: input, + content, }); } } diff --git a/js/plugins/google-cloud/src/telemetry/defaults.ts b/js/plugins/google-cloud/src/telemetry/defaults.ts index 4683fc9ae..eb7a2f38c 100644 --- a/js/plugins/google-cloud/src/telemetry/defaults.ts +++ b/js/plugins/google-cloud/src/telemetry/defaults.ts @@ -41,7 +41,7 @@ export const TelemetryConfigs = { metricExportTimeoutMillis: 5_000, disableMetrics: false, disableTraces: false, - exportIO: !overrides.disableLoggingIO, + exportInputAndOutput: !overrides.disableLoggingInputAndOutput, export: !!overrides.forceDevExport, // false }; return { ...defaults, ...overrides }; @@ -61,8 +61,7 @@ export const TelemetryConfigs = { metricExportTimeoutMillis: 300_000, disableMetrics: false, disableTraces: false, - disableLoggingIO: false, - exportIO: !overrides.disableLoggingIO, + exportInputAndOutput: !overrides.disableLoggingInputAndOutput, export: true, }; return { ...defaults, ...overrides }; diff --git a/js/plugins/google-cloud/src/telemetry/engagement.ts b/js/plugins/google-cloud/src/telemetry/engagement.ts index ce3664682..77daef21d 100644 --- a/js/plugins/google-cloud/src/telemetry/engagement.ts +++ b/js/plugins/google-cloud/src/telemetry/engagement.ts @@ -45,7 +45,7 @@ class EngagementTelemetry implements Telemetry { tick( span: ReadableSpan, paths: Set, - logIO: boolean, + logInputAndOutput: boolean, projectId?: string ): void { const subtype = span.attributes['genkit:metadata:subtype'] as string; diff --git a/js/plugins/google-cloud/src/telemetry/feature.ts b/js/plugins/google-cloud/src/telemetry/feature.ts index a893fba97..932bd40ae 100644 --- a/js/plugins/google-cloud/src/telemetry/feature.ts +++ b/js/plugins/google-cloud/src/telemetry/feature.ts @@ -48,7 +48,7 @@ class FeaturesTelemetry implements Telemetry { tick( span: ReadableSpan, paths: Set, - logIO: boolean, + logInputAndOutput: boolean, projectId?: string ): void { const attributes = span.attributes; @@ -76,14 +76,14 @@ class FeaturesTelemetry implements Telemetry { return; } - if (logIO) { + if (logInputAndOutput) { const input = attributes['genkit:input'] as string; const output = attributes['genkit:output'] as string; const sessionId = attributes['genkit:sessionId'] as string; const threadName = attributes['genkit:threadName'] as string; if (input) { - this.recordIO( + this.writeLog( span, 'Input', name, @@ -95,7 +95,7 @@ class FeaturesTelemetry implements Telemetry { ); } if (output) { - this.recordIO( + this.writeLog( span, 'Output', name, @@ -136,12 +136,12 @@ class FeaturesTelemetry implements Telemetry { this.featureLatencies.record(latencyMs, dimensions); } - private recordIO( + private writeLog( span: ReadableSpan, tag: string, featureName: string, qualifiedPath: string, - input: string, + content: string, projectId?: string, sessionId?: string, threadName?: string @@ -157,7 +157,7 @@ class FeaturesTelemetry implements Telemetry { }; logger.logStructured(`${tag}[${path}, ${featureName}]`, { ...sharedMetadata, - content: input, + content, }); } } diff --git a/js/plugins/google-cloud/src/telemetry/generate.ts b/js/plugins/google-cloud/src/telemetry/generate.ts index c1d5591ac..484a322f6 100644 --- a/js/plugins/google-cloud/src/telemetry/generate.ts +++ b/js/plugins/google-cloud/src/telemetry/generate.ts @@ -132,7 +132,7 @@ class GenerateTelemetry implements Telemetry { tick( span: ReadableSpan, paths: Set, - logIO: boolean, + logInputAndOutput: boolean, projectId?: string ): void { const attributes = span.attributes; @@ -177,7 +177,7 @@ class GenerateTelemetry implements Telemetry { threadName ); - if (logIO) { + if (logInputAndOutput) { this.recordGenerateActionInputLogs( span, modelName, @@ -191,7 +191,7 @@ class GenerateTelemetry implements Telemetry { } } - if (output && logIO) { + if (output && logInputAndOutput) { this.recordGenerateActionOutputLogs( span, modelName, diff --git a/js/plugins/google-cloud/src/telemetry/path.ts b/js/plugins/google-cloud/src/telemetry/path.ts index 411a7d2c5..79e9fa5b1 100644 --- a/js/plugins/google-cloud/src/telemetry/path.ts +++ b/js/plugins/google-cloud/src/telemetry/path.ts @@ -53,7 +53,7 @@ class PathsTelemetry implements Telemetry { tick( span: ReadableSpan, paths: Set, - logIO: boolean, + logInputAndOutput: boolean, projectId?: string ): void { const attributes = span.attributes; diff --git a/js/plugins/google-cloud/src/types.ts b/js/plugins/google-cloud/src/types.ts index 961c8637c..8c63a0473 100644 --- a/js/plugins/google-cloud/src/types.ts +++ b/js/plugins/google-cloud/src/types.ts @@ -46,7 +46,7 @@ export interface GcpTelemetryConfigOptions { disableTraces?: boolean; /** When true, inputs and outputs are not logged to GCP */ - disableLoggingIO?: boolean; + disableLoggingInputAndOutput?: boolean; /** When true, telemetry data will be exported, even for local runs. Defaults to not exporting development traces. */ forceDevExport?: boolean; @@ -67,7 +67,7 @@ export interface GcpTelemetryConfig { instrumentations: Instrumentation[]; disableMetrics: boolean; disableTraces: boolean; - exportIO: boolean; + exportInputAndOutput: boolean; export: boolean; } diff --git a/js/plugins/google-cloud/tests/logs_no_io_test.ts b/js/plugins/google-cloud/tests/logs_no_input_output_test.ts similarity index 99% rename from js/plugins/google-cloud/tests/logs_no_io_test.ts rename to js/plugins/google-cloud/tests/logs_no_input_output_test.ts index 71a26ae2e..795f7f45a 100644 --- a/js/plugins/google-cloud/tests/logs_no_io_test.ts +++ b/js/plugins/google-cloud/tests/logs_no_input_output_test.ts @@ -74,7 +74,7 @@ describe('GoogleCloudLogs no I/O', () => { forceDevExport: false, metricExportIntervalMillis: 100, metricExportTimeoutMillis: 100, - disableLoggingIO: true, + disableLoggingInputAndOutput: true, }); ai = genkit({}); // Wait for the telemetry plugin to be initialized From c0167dd15c3b890939ea75d20b08efcffc03d8e8 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:51:23 +0000 Subject: [PATCH 299/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.7 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index b92e6ed3a..3b17ed5ea 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 43427d0dfbc109e7d94a15e05bfd1325e402001c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:51:26 +0000 Subject: [PATCH 300/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.7 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 1da5b7c96..4f9675ebe 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From c95a1a0d928f6c3a589daa98ced73fe22a69f81f Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:51:28 +0000 Subject: [PATCH 301/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.7 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index 41971412d..7b8c6fcd4 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From 7c0e9996d5139a7a72c574c93d69f28a8ca050a0 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:17 +0000 Subject: [PATCH 302/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.7 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 8d67ce264..ca88184f0 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From 2df60da1c53bddcae4488c5254fceab4ab0cf13d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:20 +0000 Subject: [PATCH 303/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.7 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index b806d6b5e..0fa3000ed 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From 464362c8351ce934192bc43c7424c9213663073f Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:22 +0000 Subject: [PATCH 304/562] chore: bump genkit version to genkit@1.0.0-rc.7 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index e2b5cb66d..006375703 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From 3c6185cb59be5bce185515fdaefa9081d2a7330a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:25 +0000 Subject: [PATCH 305/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.7 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index 652289b85..30ad67f01 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From 3cc2dfa93bb5816cb1bc20639a3e2d67270a3a80 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:27 +0000 Subject: [PATCH 306/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.7 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 5ab5953d1..8979c1db4 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From 159c2550d0a139c4bc8f54917659aec9f2f27d5a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:30 +0000 Subject: [PATCH 307/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.7 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 06aba01d4..04270ca4b 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From 116f9ef215db666f1f2601ba25732589ca15f9ba Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:32 +0000 Subject: [PATCH 308/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.7 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 58e4b6cdf..783c260d0 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From 9f8178b8823b6a4f29c581d4194265e3afa80dc8 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:34 +0000 Subject: [PATCH 309/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.7 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index e8f73aefd..ff620a24f 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From 8dc5647b79ce8eac7a8b3b15043ceaea026ea259 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:37 +0000 Subject: [PATCH 310/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.7 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 28a550d73..9bec413f9 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From 7c0e4c02f5dc01abc2b0993008222e6f2a7094b6 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:39 +0000 Subject: [PATCH 311/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.7 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index 9169052e8..c67e8ce0c 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From 883025a0d361144e26d8b6725f5b2c876047699d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:42 +0000 Subject: [PATCH 312/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.7 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index 2894c471c..eb6ca0cd1 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From 4eb912d6ec6cae2d92121445359b2c3a62102d00 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:44 +0000 Subject: [PATCH 313/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.7 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index ea6c45176..4e629f11f 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From 668a59b050e5ec2eda9402a58702e380bfc15634 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:46 +0000 Subject: [PATCH 314/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.7 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 11180a8e6..25a6f9c59 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From 1c45798ce3851e8aff4388afc0e8fbefad1e4ef9 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:49 +0000 Subject: [PATCH 315/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.7 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index ad3116477..ced0ba2ad 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From 7f77c623083c0ba1f6d7e97c7c6f7d296f5b24c6 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:51 +0000 Subject: [PATCH 316/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.7 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index 549516e60..67d779710 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From 241eca19456e28eb0599670027c8e251588620d8 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 28 Jan 2025 18:52:54 +0000 Subject: [PATCH 317/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.7 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index 0ccc4d99b..fc5dff279 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.6", + "version": "1.0.0-rc.7", "type": "commonjs", "scripts": { "check": "tsc", From f771771094a6dc7b1f5d0cfddd0f7321a733fefe Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Tue, 28 Jan 2025 19:12:43 -0500 Subject: [PATCH 318/562] fix(ai/core): correctly allow plugin initialization from runtime context (#1678) --- js/core/src/action.ts | 12 ++++++++- js/core/src/plugin.ts | 48 ---------------------------------- js/core/src/registry.ts | 6 +++-- js/core/tests/action_test.ts | 2 +- js/core/tests/registry_test.ts | 32 ++++++++++++++++++++++- 5 files changed, 47 insertions(+), 53 deletions(-) diff --git a/js/core/src/action.ts b/js/core/src/action.ts index 30e1ac7dd..b04f5c99b 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -512,7 +512,7 @@ const runtimeContextAslKey = 'core.action.runtimeContext'; * Checks whether the caller is currently in the runtime context of an action. */ export function isInRuntimeContext(registry: Registry) { - return !!registry.asyncStore.getStore(runtimeContextAslKey); + return registry.asyncStore.getStore(runtimeContextAslKey) === 'runtime'; } /** @@ -521,3 +521,13 @@ export function isInRuntimeContext(registry: Registry) { export function runInActionRuntimeContext(registry: Registry, fn: () => R) { return registry.asyncStore.run(runtimeContextAslKey, 'runtime', fn); } + +/** + * Execute the provided function outside the action runtime context. + */ +export function runOutsideActionRuntimeContext( + registry: Registry, + fn: () => R +) { + return registry.asyncStore.run(runtimeContextAslKey, 'outside', fn); +} diff --git a/js/core/src/plugin.ts b/js/core/src/plugin.ts index ae5bfe340..5f5b9d1d2 100644 --- a/js/core/src/plugin.ts +++ b/js/core/src/plugin.ts @@ -44,52 +44,4 @@ export interface InitializedPlugin { telemetry?: any; } -type PluginInit = ( - ...args: any[] -) => InitializedPlugin | void | Promise; - export type Plugin = (...args: T) => PluginProvider; - -/** - * Defines a Genkit plugin. - */ -export function genkitPlugin( - pluginName: string, - initFn: T -): Plugin> { - return (...args: Parameters) => ({ - name: pluginName, - initializer: async () => { - const initializedPlugin = (await initFn(...args)) || {}; - validatePluginActions(pluginName, initializedPlugin); - return initializedPlugin; - }, - }); -} - -function validatePluginActions(pluginName: string, plugin?: InitializedPlugin) { - if (!plugin) { - return; - } - - plugin.models?.forEach((model) => validateNaming(pluginName, model)); - plugin.retrievers?.forEach((retriever) => - validateNaming(pluginName, retriever) - ); - plugin.embedders?.forEach((embedder) => validateNaming(pluginName, embedder)); - plugin.indexers?.forEach((indexer) => validateNaming(pluginName, indexer)); - plugin.evaluators?.forEach((evaluator) => - validateNaming(pluginName, evaluator) - ); -} - -function validateNaming( - pluginName: string, - action: Action -) { - const nameParts = action.__action.name.split('/'); - if (nameParts[0] !== pluginName) { - const err = `Plugin name ${pluginName} not found in action name ${action.__action.name}. Action names must follow the pattern {pluginName}/{actionName}`; - throw new Error(err); - } -} diff --git a/js/core/src/registry.ts b/js/core/src/registry.ts index 1768cb0cf..96d95ada7 100644 --- a/js/core/src/registry.ts +++ b/js/core/src/registry.ts @@ -17,7 +17,7 @@ import { Dotprompt } from 'dotprompt'; import { AsyncLocalStorage } from 'node:async_hooks'; import * as z from 'zod'; -import { Action } from './action.js'; +import { Action, runOutsideActionRuntimeContext } from './action.js'; import { GenkitError } from './error.js'; import { logger } from './logging.js'; import { PluginProvider } from './plugin.js'; @@ -229,7 +229,9 @@ export class Registry { */ async initializePlugin(name: string) { if (this.pluginsByName[name]) { - return await this.pluginsByName[name].initializer(); + return await runOutsideActionRuntimeContext(this, () => + this.pluginsByName[name].initializer() + ); } } diff --git a/js/core/tests/action_test.ts b/js/core/tests/action_test.ts index 76f5c1d84..bdaa419c1 100644 --- a/js/core/tests/action_test.ts +++ b/js/core/tests/action_test.ts @@ -150,7 +150,7 @@ describe('action', () => { registry, { name: 'child', actionType: 'custom' }, async (_, { context }) => { - return `hi ${context.auth.email}`; + return `hi ${context?.auth?.email}`; } ); const parent = defineAction( diff --git a/js/core/tests/registry_test.ts b/js/core/tests/registry_test.ts index 357620ca6..24d15e4ba 100644 --- a/js/core/tests/registry_test.ts +++ b/js/core/tests/registry_test.ts @@ -16,7 +16,11 @@ import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; -import { action } from '../src/action.js'; +import { + action, + defineAction, + runInActionRuntimeContext, +} from '../src/action.js'; import { Registry } from '../src/registry.js'; describe('registry class', () => { @@ -103,6 +107,32 @@ describe('registry class', () => { }); }); + it('should allow plugin initialization from runtime context', async () => { + let fooInitialized = false; + registry.registerPluginProvider('foo', { + name: 'foo', + async initializer() { + defineAction( + registry, + { + actionType: 'model', + name: 'foo/something', + }, + async () => null + ); + fooInitialized = true; + return {}; + }, + }); + + const action = await runInActionRuntimeContext(registry, () => + registry.lookupAction('/model/foo/something') + ); + + assert.ok(action); + assert.ok(fooInitialized); + }); + it('returns all registered actions, including parent', async () => { const child = Registry.withParent(registry); From 043d9338088ac6ace27dc4d45c0c5cea185a4465 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:15:00 +0000 Subject: [PATCH 319/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.8 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 3b17ed5ea..6bc948db4 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 2232ed898704225bf7ebf4c82b077b7d2d9f433c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:15:03 +0000 Subject: [PATCH 320/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.8 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 4f9675ebe..c761bd3ef 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From eb7cafb5dbb4a7967721230575422f8cdeffe82f Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:15:05 +0000 Subject: [PATCH 321/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.8 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index 7b8c6fcd4..c7e53c2fc 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From 715ea00e842a078277264d83906a64143ea10aae Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:01 +0000 Subject: [PATCH 322/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.8 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index ca88184f0..5be601f94 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From 9ff19017dfe662553e907699d713445296e4fca2 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:04 +0000 Subject: [PATCH 323/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.8 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index 0fa3000ed..6dcff73b7 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From d3dae782fd5d03a2de1ac205f4f1d52a3edcde95 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:07 +0000 Subject: [PATCH 324/562] chore: bump genkit version to genkit@1.0.0-rc.8 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index 006375703..bb6df6955 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From 491d48b6e32b01c75f0adad87e04adef056b3d08 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:09 +0000 Subject: [PATCH 325/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.8 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index 30ad67f01..61b42e09d 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From 5b42d1b0b13cd64b5b71f45f5021f2fa9afac3d1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:12 +0000 Subject: [PATCH 326/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.8 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 8979c1db4..782eda705 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From 604ac2d22f610e513526712769367aef05b0f033 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:15 +0000 Subject: [PATCH 327/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.8 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 04270ca4b..0427f1c05 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From 6759458fe2b20dd8f70471fde692840c052431e5 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:18 +0000 Subject: [PATCH 328/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.8 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 783c260d0..0645a8170 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From 0001a092965543d8f4a7edbec75e780701175f32 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:21 +0000 Subject: [PATCH 329/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.8 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index ff620a24f..b8d0c0db8 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From 420dacd070d50e1e6f65f7bb553d23a9dde299d3 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:23 +0000 Subject: [PATCH 330/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.8 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 9bec413f9..7d022e53b 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From 15f025da548b8f47d3b01109ad80d0db6dee36d1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:26 +0000 Subject: [PATCH 331/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.8 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index c67e8ce0c..df5d8e85a 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From 60ef5c3b36bb17db04e88211a40d98d61147d9bb Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:29 +0000 Subject: [PATCH 332/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.8 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index eb6ca0cd1..b7787094d 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From 301a8ba1716f64c7c8b833e986876050175e9cd9 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:31 +0000 Subject: [PATCH 333/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.8 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index 4e629f11f..a1fac3596 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From b129ac4db8d8e6116f6cd2d841448c5dd51b332c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:35 +0000 Subject: [PATCH 334/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.8 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 25a6f9c59..5a3abae5a 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From 2aaf2e5506953dcd1e265ef3d0412fb3d476026a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:37 +0000 Subject: [PATCH 335/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.8 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index ced0ba2ad..234ca49dd 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From bf06cb051a1795881b33a5db451bc29907645c02 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:40 +0000 Subject: [PATCH 336/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.8 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index 67d779710..b10dace2d 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From 8fcfbfd1405a92fe3e708767c617b9c05e9d5c7b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 00:16:43 +0000 Subject: [PATCH 337/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.8 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index fc5dff279..053690a58 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.7", + "version": "1.0.0-rc.8", "type": "commonjs", "scripts": { "check": "tsc", From c7b916551a3e9d4621f33ab7ca700a6cbd74f85e Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Tue, 28 Jan 2025 16:27:29 -0800 Subject: [PATCH 338/562] chore(js/genkit): Export GenerateResponseChunk and GenerateResponseChunkData properly. --- js/ai/src/index.ts | 2 ++ js/genkit/src/index.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/js/ai/src/index.ts b/js/ai/src/index.ts index 8d0cd6c37..1c8ad5a8d 100644 --- a/js/ai/src/index.ts +++ b/js/ai/src/index.ts @@ -51,6 +51,7 @@ export { } from './generate.js'; export { Message } from './message.js'; export { + GenerateResponseChunkSchema, GenerationCommonConfigSchema, MessageSchema, ModelRequestSchema, @@ -59,6 +60,7 @@ export { RoleSchema, type GenerateRequest, type GenerateRequestData, + type GenerateResponseChunkData, type GenerateResponseData, type GenerationUsage, type MediaPart, diff --git a/js/genkit/src/index.ts b/js/genkit/src/index.ts index 204b60a59..938711d4d 100644 --- a/js/genkit/src/index.ts +++ b/js/genkit/src/index.ts @@ -63,6 +63,8 @@ export { type GenerateRequest, type GenerateRequestData, type GenerateResponse, + type GenerateResponseChunk, + type GenerateResponseChunkData, type GenerateResponseData, type GenerateStreamOptions, type GenerateStreamResponse, From 51ceb793bdb0d15a1b0790add6115ef694b75401 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Tue, 28 Jan 2025 22:28:58 -0500 Subject: [PATCH 339/562] feat(js): added /util/generate action to be exposed to the dev ui (#1675) --- genkit-tools/common/src/types/model.ts | 29 +++ genkit-tools/genkit-schema.json | 154 ++++++++---- js/ai/src/generate.ts | 3 +- js/ai/src/generate/action.ts | 77 +++--- js/ai/src/generate/resolve-tool-requests.ts | 7 +- js/ai/src/model.ts | 30 +++ js/ai/tests/formats/json_test.ts | 8 +- js/ai/tests/generate/action_test.ts | 252 ++++++++++++++++++++ js/ai/tests/helpers.ts | 60 +++++ js/ai/tests/prompt/prompt_test.ts | 62 +---- js/genkit/src/genkit.ts | 2 + 11 files changed, 539 insertions(+), 145 deletions(-) create mode 100644 js/ai/tests/generate/action_test.ts diff --git a/genkit-tools/common/src/types/model.ts b/genkit-tools/common/src/types/model.ts index d1f4a74dc..379b84f52 100644 --- a/genkit-tools/common/src/types/model.ts +++ b/genkit-tools/common/src/types/model.ts @@ -274,3 +274,32 @@ export const GenerateResponseChunkSchema = ModelResponseChunkSchema.extend({}); export type GenerateResponseChunkData = z.infer< typeof GenerateResponseChunkSchema >; + +export const GenerateActionOptionsSchema = z.object({ + /** A model name (e.g. `vertexai/gemini-1.0-pro`). */ + model: z.string(), + /** Retrieved documents to be used as context for this generation. */ + docs: z.array(DocumentDataSchema).optional(), + /** Conversation history for multi-turn prompting when supported by the underlying model. */ + messages: z.array(MessageSchema), + /** List of registered tool names for this generation if supported by the underlying model. */ + tools: z.array(z.union([z.string(), ToolDefinitionSchema])).optional(), + /** Tool calling mode. `auto` lets the model decide whether to use tools, `required` forces the model to choose a tool, and `none` forces the model not to use any tools. Defaults to `auto`. */ + toolChoice: z.enum(['auto', 'required', 'none']).optional(), + /** Configuration for the generation request. */ + config: z.any().optional(), + /** Configuration for the desired output of the request. Defaults to the model's default output if unspecified. */ + output: z + .object({ + format: z.string().optional(), + contentType: z.string().optional(), + instructions: z.union([z.boolean(), z.string()]).optional(), + jsonSchema: z.any().optional(), + }) + .optional(), + /** When true, return tool calls for manual processing instead of automatically resolving them. */ + returnToolRequests: z.boolean().optional(), + /** Maximum number of tool call iterations that can be performed in a single generate call (default 5). */ + maxTurns: z.number().optional(), +}); +export type GenerateActionOptions = z.infer; diff --git a/genkit-tools/genkit-schema.json b/genkit-tools/genkit-schema.json index 9cddfd60b..b9bba91b9 100644 --- a/genkit-tools/genkit-schema.json +++ b/genkit-tools/genkit-schema.json @@ -345,49 +345,13 @@ "unknown" ] }, - "GenerateRequest": { + "GenerateActionOptions": { "type": "object", "properties": { - "messages": { - "type": "array", - "items": { - "$ref": "#/$defs/Message" - } - }, - "config": {}, - "tools": { - "type": "array", - "items": { - "$ref": "#/$defs/ToolDefinition" - } - }, - "toolChoice": { - "type": "string", - "enum": [ - "auto", - "required", - "none" - ] - }, - "output": { - "type": "object", - "properties": { - "format": { - "type": "string", - "enum": [ - "json", - "text", - "media" - ] - }, - "schema": { - "type": "object", - "additionalProperties": {} - } - }, - "additionalProperties": false + "model": { + "type": "string" }, - "context": { + "docs": { "type": "array", "items": { "type": "object", @@ -452,6 +416,114 @@ "additionalProperties": false } }, + "messages": { + "type": "array", + "items": { + "$ref": "#/$defs/Message" + } + }, + "tools": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/$defs/ToolDefinition" + } + ] + } + }, + "toolChoice": { + "type": "string", + "enum": [ + "auto", + "required", + "none" + ] + }, + "config": {}, + "output": { + "type": "object", + "properties": { + "format": { + "type": "string" + }, + "contentType": { + "type": "string" + }, + "instructions": { + "type": [ + "boolean", + "string" + ] + }, + "jsonSchema": {} + }, + "additionalProperties": false + }, + "returnToolRequests": { + "type": "boolean" + }, + "maxTurns": { + "type": "number" + } + }, + "required": [ + "model", + "messages" + ], + "additionalProperties": false + }, + "GenerateRequest": { + "type": "object", + "properties": { + "messages": { + "type": "array", + "items": { + "$ref": "#/$defs/Message" + } + }, + "config": {}, + "tools": { + "type": "array", + "items": { + "$ref": "#/$defs/ToolDefinition" + } + }, + "toolChoice": { + "type": "string", + "enum": [ + "auto", + "required", + "none" + ] + }, + "output": { + "type": "object", + "properties": { + "format": { + "type": "string", + "enum": [ + "json", + "text", + "media" + ] + }, + "schema": { + "type": "object", + "additionalProperties": {} + } + }, + "additionalProperties": false + }, + "context": { + "type": "array", + "items": { + "$ref": "#/$defs/GenerateActionOptions/properties/docs/items" + } + }, "candidates": { "type": "number" } @@ -721,7 +793,7 @@ "context": { "type": "array", "items": { - "$ref": "#/$defs/GenerateRequest/properties/context/items" + "$ref": "#/$defs/GenerateActionOptions/properties/docs/items" } } }, diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index f1f84bfb8..89b4a7abd 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -34,11 +34,12 @@ import { resolveFormat, resolveInstructions, } from './formats/index.js'; -import { GenerateActionOptions, generateHelper } from './generate/action.js'; +import { generateHelper } from './generate/action.js'; import { GenerateResponseChunk } from './generate/chunk.js'; import { GenerateResponse } from './generate/response.js'; import { Message } from './message.js'; import { + GenerateActionOptions, GenerateRequest, GenerationCommonConfigSchema, MessageData, diff --git a/js/ai/src/generate/action.ts b/js/ai/src/generate/action.ts index 67d4a3202..41d84006e 100644 --- a/js/ai/src/generate/action.ts +++ b/js/ai/src/generate/action.ts @@ -15,6 +15,8 @@ */ import { + Action, + defineAction, getStreamingCallback, runWithStreamingCallback, stripUndefinedProps, @@ -24,7 +26,6 @@ import { logger } from '@genkit-ai/core/logging'; import { Registry } from '@genkit-ai/core/registry'; import { toJsonSchema } from '@genkit-ai/core/schema'; import { SPAN_TYPE_ATTR, runInNewSpan } from '@genkit-ai/core/tracing'; -import { DocumentDataSchema } from '../document.js'; import { injectInstructions, resolveFormat, @@ -38,18 +39,20 @@ import { tagAsPreamble, } from '../generate.js'; import { + GenerateActionOptions, + GenerateActionOptionsSchema, GenerateRequest, GenerateRequestSchema, GenerateResponseChunkData, + GenerateResponseChunkSchema, GenerateResponseData, - MessageSchema, + GenerateResponseSchema, ModelAction, ModelInfo, ModelMiddleware, ModelRequest, Part, Role, - ToolDefinitionSchema, resolveModel, } from '../model.js'; import { ToolAction, resolveTools, toToolDefinition } from '../tool.js'; @@ -58,34 +61,42 @@ import { resolveToolRequests, } from './resolve-tool-requests.js'; -export const GenerateActionOptionsSchema = z.object({ - /** A model name (e.g. `vertexai/gemini-1.0-pro`). */ - model: z.string(), - /** Retrieved documents to be used as context for this generation. */ - docs: z.array(DocumentDataSchema).optional(), - /** Conversation history for multi-turn prompting when supported by the underlying model. */ - messages: z.array(MessageSchema), - /** List of registered tool names for this generation if supported by the underlying model. */ - tools: z.array(z.union([z.string(), ToolDefinitionSchema])).optional(), - /** Tool calling mode. `auto` lets the model decide whether to use tools, `required` forces the model to choose a tool, and `none` forces the model not to use any tools. Defaults to `auto`. */ - toolChoice: z.enum(['auto', 'required', 'none']).optional(), - /** Configuration for the generation request. */ - config: z.any().optional(), - /** Configuration for the desired output of the request. Defaults to the model's default output if unspecified. */ - output: z - .object({ - format: z.string().optional(), - contentType: z.string().optional(), - instructions: z.union([z.boolean(), z.string()]).optional(), - jsonSchema: z.any().optional(), - }) - .optional(), - /** When true, return tool calls for manual processing instead of automatically resolving them. */ - returnToolRequests: z.boolean().optional(), - /** Maximum number of tool call iterations that can be performed in a single generate call (default 5). */ - maxTurns: z.number().optional(), -}); -export type GenerateActionOptions = z.infer; +export type GenerateAction = Action< + typeof GenerateActionOptionsSchema, + typeof GenerateResponseSchema, + typeof GenerateResponseChunkSchema +>; + +/** Defines (registers) a utilty generate action. */ +export function defineGenerateAction(registry: Registry): GenerateAction { + return defineAction( + registry, + { + actionType: 'util', + name: 'generate', + inputSchema: GenerateActionOptionsSchema, + outputSchema: GenerateResponseSchema, + streamSchema: GenerateResponseChunkSchema, + }, + async (request, { sendChunk }) => { + const generateFn = () => + generate(registry, { + rawRequest: request, + currentTurn: 0, + messageIndex: 0, + // Generate util action does not support middleware. Maybe when we add named/registered middleware.... + middleware: [], + }); + return sendChunk + ? runWithStreamingCallback( + registry, + (c: GenerateResponseChunk) => sendChunk(c.toJSON ? c.toJSON() : c), + generateFn + ) + : generateFn(); + } + ); +} /** * Encapsulates all generate logic. This is similar to `generateAction` except not an action and can take middleware. @@ -109,7 +120,7 @@ export async function generateHelper( name: 'generate', }, labels: { - [SPAN_TYPE_ATTR]: 'helper', + [SPAN_TYPE_ATTR]: 'util', }, }, async (metadata) => { @@ -318,7 +329,7 @@ async function generate( let nextRequest = { ...rawRequest, - messages: [...rawRequest.messages, generatedMessage, toolMessage!], + messages: [...rawRequest.messages, generatedMessage.toJSON(), toolMessage!], }; nextRequest = applyTransferPreamble(nextRequest, transferPreamble); diff --git a/js/ai/src/generate/resolve-tool-requests.ts b/js/ai/src/generate/resolve-tool-requests.ts index 7376fad50..c8ead6e28 100644 --- a/js/ai/src/generate/resolve-tool-requests.ts +++ b/js/ai/src/generate/resolve-tool-requests.ts @@ -17,10 +17,13 @@ import { GenkitError, stripUndefinedProps } from '@genkit-ai/core'; import { logger } from '@genkit-ai/core/logging'; import { Registry } from '@genkit-ai/core/registry'; -import { MessageData, ToolResponsePart } from '../model.js'; +import { + GenerateActionOptions, + MessageData, + ToolResponsePart, +} from '../model.js'; import { isPromptAction } from '../prompt.js'; import { ToolAction, ToolInterruptError, resolveTools } from '../tool.js'; -import { GenerateActionOptions } from './action.js'; export function toToolMap(tools: ToolAction[]): Record { assertValidToolNames(tools); diff --git a/js/ai/src/model.ts b/js/ai/src/model.ts index f23e90206..284f285fd 100644 --- a/js/ai/src/model.ts +++ b/js/ai/src/model.ts @@ -33,6 +33,7 @@ import { simulateConstrainedGeneration, validateSupport, } from './model/middleware.js'; +export { defineGenerateAction } from './generate/action.js'; export { simulateConstrainedGeneration }; // @@ -687,3 +688,32 @@ export async function resolveModel( return out; } + +export const GenerateActionOptionsSchema = z.object({ + /** A model name (e.g. `vertexai/gemini-1.0-pro`). */ + model: z.string(), + /** Retrieved documents to be used as context for this generation. */ + docs: z.array(DocumentDataSchema).optional(), + /** Conversation history for multi-turn prompting when supported by the underlying model. */ + messages: z.array(MessageSchema), + /** List of registered tool names for this generation if supported by the underlying model. */ + tools: z.array(z.union([z.string(), ToolDefinitionSchema])).optional(), + /** Tool calling mode. `auto` lets the model decide whether to use tools, `required` forces the model to choose a tool, and `none` forces the model not to use any tools. Defaults to `auto`. */ + toolChoice: z.enum(['auto', 'required', 'none']).optional(), + /** Configuration for the generation request. */ + config: z.any().optional(), + /** Configuration for the desired output of the request. Defaults to the model's default output if unspecified. */ + output: z + .object({ + format: z.string().optional(), + contentType: z.string().optional(), + instructions: z.union([z.boolean(), z.string()]).optional(), + jsonSchema: z.any().optional(), + }) + .optional(), + /** When true, return tool calls for manual processing instead of automatically resolving them. */ + returnToolRequests: z.boolean().optional(), + /** Maximum number of tool call iterations that can be performed in a single generate call (default 5). */ + maxTurns: z.number().optional(), +}); +export type GenerateActionOptions = z.infer; diff --git a/js/ai/tests/formats/json_test.ts b/js/ai/tests/formats/json_test.ts index 70c8a733b..eb899f794 100644 --- a/js/ai/tests/formats/json_test.ts +++ b/js/ai/tests/formats/json_test.ts @@ -23,19 +23,13 @@ import { jsonFormatter } from '../../src/formats/json.js'; import { GenerateResponseChunk, generateStream } from '../../src/generate.js'; import { Message } from '../../src/message.js'; import { GenerateResponseChunkData, MessageData } from '../../src/model.js'; -import { - ProgrammableModel, - defineProgrammableModel, - runAsync, -} from '../helpers.js'; +import { defineProgrammableModel, runAsync } from '../helpers.js'; describe('jsonFormat', () => { let registry: Registry; - let pm: ProgrammableModel; beforeEach(() => { registry = new Registry(); - pm = defineProgrammableModel(registry); }); const streamingTests = [ diff --git a/js/ai/tests/generate/action_test.ts b/js/ai/tests/generate/action_test.ts new file mode 100644 index 000000000..b41df3ed5 --- /dev/null +++ b/js/ai/tests/generate/action_test.ts @@ -0,0 +1,252 @@ +/** + * Copyright 2024 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 { Registry } from '@genkit-ai/core/registry'; +import * as assert from 'assert'; +import { beforeEach, describe, it } from 'node:test'; +import { + GenerateAction, + defineGenerateAction, +} from '../../src/generate/action.js'; +import { GenerateResponseChunkData } from '../../src/model.js'; +import { defineTool } from '../../src/tool.js'; +import { + ProgrammableModel, + defineEchoModel, + defineProgrammableModel, +} from '../helpers.js'; + +describe('generate', () => { + let registry: Registry; + let pm: ProgrammableModel; + + beforeEach(() => { + registry = new Registry(); + defineGenerateAction(registry); + defineEchoModel(registry); + pm = defineProgrammableModel(registry); + }); + + it('registers the action', async () => { + const action = await registry.lookupAction('/util/generate'); + assert.ok(action); + }); + + it('generate simple response', async () => { + const action = (await registry.lookupAction( + '/util/generate' + )) as GenerateAction; + + const response = await action({ + model: 'echoModel', + messages: [{ role: 'user', content: [{ text: 'hi' }] }], + config: { temperature: 11 }, + }); + + assert.deepStrictEqual(response, { + custom: {}, + finishReason: 'stop', + message: { + role: 'model', + content: [ + { text: 'Echo: hi' }, + { text: '; config: {"temperature":11}' }, + ], + }, + request: { + messages: [ + { + role: 'user', + content: [{ text: 'hi' }], + }, + ], + output: {}, + tools: [], + config: { + temperature: 11, + }, + docs: undefined, + }, + usage: {}, + }); + }); + + it('should call tools', async () => { + const action = (await registry.lookupAction( + '/util/generate' + )) as GenerateAction; + + defineTool( + registry, + { name: 'testTool', description: 'description' }, + async () => 'tool called' + ); + + // first response be tools call, the subsequent just text response from agent b. + let reqCounter = 0; + pm.handleResponse = async (req, sc) => { + return { + message: { + role: 'model', + content: [ + reqCounter++ === 0 + ? { + toolRequest: { + name: 'testTool', + input: {}, + ref: 'ref123', + }, + } + : { + text: req.messages + .map((m) => + m.content + .map( + (c) => + c.text || JSON.stringify(c.toolResponse?.output) + ) + .join() + ) + .join(), + }, + ], + }, + }; + }; + + const response = await action({ + model: 'programmableModel', + messages: [{ role: 'user', content: [{ text: 'hi' }] }], + tools: ['testTool'], + config: { temperature: 11 }, + }); + + assert.deepStrictEqual(response, { + custom: {}, + finishReason: undefined, + message: { + role: 'model', + content: [{ text: 'hi,,"tool called"' }], + }, + request: { + messages: [ + { + role: 'user', + content: [{ text: 'hi' }], + }, + { + content: [ + { + toolRequest: { + input: {}, + name: 'testTool', + ref: 'ref123', + }, + }, + ], + role: 'model', + }, + { + content: [ + { + toolResponse: { + name: 'testTool', + output: 'tool called', + ref: 'ref123', + }, + }, + ], + role: 'tool', + }, + ], + output: {}, + tools: [ + { + description: 'description', + inputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + name: 'testTool', + outputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + }, + ], + config: { + temperature: 11, + }, + docs: undefined, + }, + usage: {}, + }); + }); + + it('streams simple response', async () => { + const action = (await registry.lookupAction( + '/util/generate' + )) as GenerateAction; + + const { output, stream } = action.stream({ + model: 'echoModel', + messages: [{ role: 'user', content: [{ text: 'hi' }] }], + }); + + const chunks = [] as GenerateResponseChunkData[]; + for await (const chunk of stream) { + chunks.push(chunk); + } + + assert.deepStrictEqual(chunks, [ + { + index: 0, + role: 'model', + content: [{ text: '3' }], + }, + { + index: 0, + role: 'model', + content: [{ text: '2' }], + }, + { + index: 0, + role: 'model', + content: [{ text: '1' }], + }, + ]); + + assert.deepStrictEqual(await output, { + custom: {}, + finishReason: 'stop', + message: { + role: 'model', + content: [{ text: 'Echo: hi' }, { text: '; config: undefined' }], + }, + request: { + messages: [ + { + role: 'user', + content: [{ text: 'hi' }], + }, + ], + output: {}, + tools: [], + config: undefined, + docs: undefined, + }, + usage: {}, + }); + }); +}); diff --git a/js/ai/tests/helpers.ts b/js/ai/tests/helpers.ts index 174833be3..2b1374441 100644 --- a/js/ai/tests/helpers.ts +++ b/js/ai/tests/helpers.ts @@ -58,3 +58,63 @@ export function defineProgrammableModel( return pm; } + +export function defineEchoModel(registry: Registry): ModelAction { + const model = defineModel( + registry, + { + name: 'echoModel', + }, + async (request, streamingCallback) => { + (model as any).__test__lastRequest = request; + (model as any).__test__lastStreamingCallback = streamingCallback; + if (streamingCallback) { + streamingCallback({ + content: [ + { + text: '3', + }, + ], + }); + streamingCallback({ + content: [ + { + text: '2', + }, + ], + }); + streamingCallback({ + content: [ + { + text: '1', + }, + ], + }); + } + return { + message: { + role: 'model', + content: [ + { + text: + 'Echo: ' + + request.messages + .map( + (m) => + (m.role === 'user' || m.role === 'model' + ? '' + : `${m.role}: `) + m.content.map((c) => c.text).join() + ) + .join(), + }, + { + text: '; config: ' + JSON.stringify(request.config), + }, + ], + }, + finishReason: 'stop', + }; + } + ); + return model; +} diff --git a/js/ai/tests/prompt/prompt_test.ts b/js/ai/tests/prompt/prompt_test.ts index 214f454be..3cab2b60c 100644 --- a/js/ai/tests/prompt/prompt_test.ts +++ b/js/ai/tests/prompt/prompt_test.ts @@ -21,7 +21,6 @@ import assert from 'node:assert'; import { beforeEach, describe, it } from 'node:test'; import { Document } from '../../src/document.js'; import { GenerateOptions } from '../../src/index.js'; -import { ModelAction, defineModel } from '../../src/model'; import { PromptConfig, PromptGenerateOptions, @@ -29,6 +28,7 @@ import { } from '../../src/prompt.js'; import { Session } from '../../src/session.js'; import { defineTool } from '../../src/tool.js'; +import { defineEchoModel } from '../helpers.js'; describe('prompt', () => { let registry; @@ -823,66 +823,6 @@ describe('prompt', () => { }); }); -export function defineEchoModel(registry: Registry): ModelAction { - const model = defineModel( - registry, - { - name: 'echoModel', - }, - async (request, streamingCallback) => { - (model as any).__test__lastRequest = request; - (model as any).__test__lastStreamingCallback = streamingCallback; - if (streamingCallback) { - streamingCallback({ - content: [ - { - text: '3', - }, - ], - }); - streamingCallback({ - content: [ - { - text: '2', - }, - ], - }); - streamingCallback({ - content: [ - { - text: '1', - }, - ], - }); - } - return { - message: { - role: 'model', - content: [ - { - text: - 'Echo: ' + - request.messages - .map( - (m) => - (m.role === 'user' || m.role === 'model' - ? '' - : `${m.role}: `) + m.content.map((c) => c.text).join() - ) - .join(), - }, - { - text: '; config: ' + JSON.stringify(request.config), - }, - ], - }, - finishReason: 'stop', - }; - } - ); - return model; -} - function stripUndefined(input: any) { if ( input === undefined || diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index 48afd4bec..e27de15e8 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -79,6 +79,7 @@ import { DefineModelOptions, GenerateResponseChunkData, ModelAction, + defineGenerateAction, defineModel, } from '@genkit-ai/ai/model'; import { @@ -801,6 +802,7 @@ export class Genkit implements HasRegistry { */ private configure() { const activeRegistry = this.registry; + defineGenerateAction(activeRegistry); // install the default formats in the registry configureFormats(activeRegistry); const plugins = [...(this.options.plugins ?? [])]; From 94ea90c4551e697ca314f79a01474149ab23a8f6 Mon Sep 17 00:00:00 2001 From: Konstantin Mandrika Date: Wed, 29 Jan 2025 09:54:07 -0500 Subject: [PATCH 340/562] feat(js): truncate input/output to align with GCP limits (#1670) Truncate the input and output logs, as well as model names, paths, errors, stacks, and other bits to ensure that the logs size remains within the GCP limits. --- .../google-cloud/src/telemetry/action.ts | 8 +- .../google-cloud/src/telemetry/engagement.ts | 6 +- .../google-cloud/src/telemetry/feature.ts | 13 +- .../google-cloud/src/telemetry/generate.ts | 42 ++-- js/plugins/google-cloud/src/telemetry/path.ts | 5 +- js/plugins/google-cloud/src/utils.ts | 28 ++- js/plugins/google-cloud/tests/logs_test.ts | 203 ++++++++++++++++++ 7 files changed, 268 insertions(+), 37 deletions(-) diff --git a/js/plugins/google-cloud/src/telemetry/action.ts b/js/plugins/google-cloud/src/telemetry/action.ts index e3630bb1d..241a8f738 100644 --- a/js/plugins/google-cloud/src/telemetry/action.ts +++ b/js/plugins/google-cloud/src/telemetry/action.ts @@ -30,6 +30,8 @@ import { createCommonLogAttributes, extractErrorName, extractOuterFeatureNameFromPath, + truncate, + truncatePath, } from '../utils.js'; class ActionTelemetry implements Telemetry { @@ -79,8 +81,8 @@ class ActionTelemetry implements Telemetry { } if (subtype === 'tool' && logInputAndOutput) { - const input = attributes['genkit:input'] as string; - const output = attributes['genkit:output'] as string; + const input = truncate(attributes['genkit:input'] as string); + const output = truncate(attributes['genkit:output'] as string); const sessionId = attributes['genkit:sessionId'] as string; const threadName = attributes['genkit:threadName'] as string; @@ -159,7 +161,7 @@ class ActionTelemetry implements Telemetry { sessionId?: string, threadName?: string ) { - const path = toDisplayPath(qualifiedPath); + const path = truncatePath(toDisplayPath(qualifiedPath)); const sharedMetadata = { ...createCommonLogAttributes(span, projectId), path, diff --git a/js/plugins/google-cloud/src/telemetry/engagement.ts b/js/plugins/google-cloud/src/telemetry/engagement.ts index 77daef21d..cdf8d91cb 100644 --- a/js/plugins/google-cloud/src/telemetry/engagement.ts +++ b/js/plugins/google-cloud/src/telemetry/engagement.ts @@ -24,7 +24,7 @@ import { Telemetry, internalMetricNamespaceWrap, } from '../metrics.js'; -import { createCommonLogAttributes } from '../utils.js'; +import { createCommonLogAttributes, truncate } from '../utils.js'; class EngagementTelemetry implements Telemetry { /** @@ -81,7 +81,9 @@ class EngagementTelemetry implements Telemetry { feedbackValue: attributes['genkit:metadata:feedbackValue'], }; if (attributes['genkit:metadata:textFeedback']) { - metadata['textFeedback'] = attributes['genkit:metadata:textFeedback']; + metadata['textFeedback'] = truncate( + attributes['genkit:metadata:textFeedback'] as string + ); } logger.logStructured(`UserFeedback[${name}]`, metadata); } diff --git a/js/plugins/google-cloud/src/telemetry/feature.ts b/js/plugins/google-cloud/src/telemetry/feature.ts index 932bd40ae..2c3e3c980 100644 --- a/js/plugins/google-cloud/src/telemetry/feature.ts +++ b/js/plugins/google-cloud/src/telemetry/feature.ts @@ -26,7 +26,12 @@ import { Telemetry, internalMetricNamespaceWrap, } from '../metrics.js'; -import { createCommonLogAttributes, extractErrorName } from '../utils.js'; +import { + createCommonLogAttributes, + extractErrorName, + truncate, + truncatePath, +} from '../utils.js'; class FeaturesTelemetry implements Telemetry { /** @@ -77,8 +82,8 @@ class FeaturesTelemetry implements Telemetry { } if (logInputAndOutput) { - const input = attributes['genkit:input'] as string; - const output = attributes['genkit:output'] as string; + const input = truncate(attributes['genkit:input'] as string); + const output = truncate(attributes['genkit:output'] as string); const sessionId = attributes['genkit:sessionId'] as string; const threadName = attributes['genkit:threadName'] as string; @@ -146,7 +151,7 @@ class FeaturesTelemetry implements Telemetry { sessionId?: string, threadName?: string ) { - const path = toDisplayPath(qualifiedPath); + const path = truncatePath(toDisplayPath(qualifiedPath)); const sharedMetadata = { ...createCommonLogAttributes(span, projectId), path, diff --git a/js/plugins/google-cloud/src/telemetry/generate.ts b/js/plugins/google-cloud/src/telemetry/generate.ts index 484a322f6..ee81594de 100644 --- a/js/plugins/google-cloud/src/telemetry/generate.ts +++ b/js/plugins/google-cloud/src/telemetry/generate.ts @@ -39,6 +39,8 @@ import { createCommonLogAttributes, extractErrorName, extractOuterFeatureNameFromPath, + truncate, + truncatePath, } from '../utils.js'; type SharedDimensions = { @@ -59,9 +61,6 @@ class GenerateTelemetry implements Telemetry { */ private _N = internalMetricNamespaceWrap.bind(null, 'ai'); - /** The maximum length (in characters) of a logged prompt message. */ - private MAX_LOG_CONTENT_CHARS = 128_000; - private actionCounter = new MetricCounter(this._N('generate/requests'), { description: 'Counts calls to genkit generate actions.', valueType: ValueType.INT, @@ -136,7 +135,7 @@ class GenerateTelemetry implements Telemetry { projectId?: string ): void { const attributes = span.attributes; - const modelName = attributes['genkit:name'] as string; + const modelName = truncate(attributes['genkit:name'] as string, 1024); const path = (attributes['genkit:path'] as string) || ''; const input = 'genkit:input' in attributes @@ -152,8 +151,10 @@ class GenerateTelemetry implements Telemetry { : undefined; const errName = extractErrorName(span.events); - let featureName = (attributes['genkit:metadata:flow:name'] || - extractOuterFeatureNameFromPath(path)) as string; + let featureName = truncate( + (attributes['genkit:metadata:flow:name'] || + extractOuterFeatureNameFromPath(path)) as string + ); if (!featureName || featureName === '') { featureName = 'generate'; } @@ -162,7 +163,7 @@ class GenerateTelemetry implements Telemetry { const threadName = attributes['genkit:threadName'] as string; if (input) { - this.recordGenerateActionMetrics(modelName, featureName, path, input, { + this.recordGenerateActionMetrics(modelName, featureName, path, { response: output, errName, }); @@ -209,7 +210,6 @@ class GenerateTelemetry implements Telemetry { modelName: string, featureName: string, path: string, - input: GenerateRequestData, opts: { response?: GenerateResponseData; errName?: string; @@ -235,7 +235,7 @@ class GenerateTelemetry implements Telemetry { sessionId?: string, threadName?: string ) { - const path = toDisplayPath(qualifiedPath); + const path = truncatePath(toDisplayPath(qualifiedPath)); const sharedMetadata = { ...createCommonLogAttributes(span, projectId), model, @@ -251,7 +251,7 @@ class GenerateTelemetry implements Telemetry { topK: input.config?.topK, topP: input.config?.topP, maxOutputTokens: input.config?.maxOutputTokens, - stopSequences: input.config?.stopSequences, + stopSequences: truncate(input.config?.stopSequences, 1024), source: 'ts', sourceVersion: GENKIT_VERSION, }); @@ -267,7 +267,7 @@ class GenerateTelemetry implements Telemetry { sessionId?: string, threadName?: string ) { - const path = toDisplayPath(qualifiedPath); + const path = truncatePath(toDisplayPath(qualifiedPath)); const sharedMetadata = { ...createCommonLogAttributes(span, projectId), model, @@ -305,7 +305,7 @@ class GenerateTelemetry implements Telemetry { sessionId?: string, threadName?: string ) { - const path = toDisplayPath(qualifiedPath); + const path = truncatePath(toDisplayPath(qualifiedPath)); const sharedMetadata = { ...createCommonLogAttributes(span, projectId), model, @@ -321,7 +321,7 @@ class GenerateTelemetry implements Telemetry { message.content.forEach((part, partIdx) => { const partCounts = this.toPartCounts(partIdx, parts, 0, 1); const initial = output.finishMessage - ? { finishMessage: this.toPartLogText(output.finishMessage) } + ? { finishMessage: truncate(output.finishMessage) } : {}; logger.logStructured(`Output[${path}, ${model}] ${partCounts}`, { ...initial, @@ -364,10 +364,10 @@ class GenerateTelemetry implements Telemetry { private toPartLogContent(part: Part): string { if (part.text) { - return this.toPartLogText(part.text); + return truncate(part.text); } if (part.data) { - return this.toPartLogText(JSON.stringify(part.data)); + return truncate(JSON.stringify(part.data)); } if (part.media) { return this.toPartLogMedia(part); @@ -379,15 +379,11 @@ class GenerateTelemetry implements Telemetry { return this.toPartLogToolResponse(part); } if (part.custom) { - return this.toPartLogText(JSON.stringify(part.custom)); + return truncate(JSON.stringify(part.custom)); } return ''; } - private toPartLogText(text: string): string { - return text.substring(0, this.MAX_LOG_CONTENT_CHARS); - } - private toPartLogMedia(part: MediaPart): string { if (part.media.url.startsWith('data:')) { const splitIdx = part.media.url.indexOf('base64,'); @@ -400,7 +396,7 @@ class GenerateTelemetry implements Telemetry { .digest('hex'); return `${prefix}`; } - return this.toPartLogText(part.media.url); + return truncate(part.media.url); } private toPartLogToolRequest(part: ToolRequestPart): string { @@ -408,7 +404,7 @@ class GenerateTelemetry implements Telemetry { typeof part.toolRequest.input === 'string' ? part.toolRequest.input : JSON.stringify(part.toolRequest.input); - return this.toPartLogText( + return truncate( `Tool request: ${part.toolRequest.name}, ref: ${part.toolRequest.ref}, input: ${inputText}` ); } @@ -418,7 +414,7 @@ class GenerateTelemetry implements Telemetry { typeof part.toolResponse.output === 'string' ? part.toolResponse.output : JSON.stringify(part.toolResponse.output); - return this.toPartLogText( + return truncate( `Tool response: ${part.toolResponse.name}, ref: ${part.toolResponse.ref}, output: ${outputText}` ); } diff --git a/js/plugins/google-cloud/src/telemetry/path.ts b/js/plugins/google-cloud/src/telemetry/path.ts index 79e9fa5b1..495cc2105 100644 --- a/js/plugins/google-cloud/src/telemetry/path.ts +++ b/js/plugins/google-cloud/src/telemetry/path.ts @@ -31,6 +31,7 @@ import { extractErrorMessage, extractErrorName, extractErrorStack, + truncatePath, } from '../utils.js'; class PathsTelemetry implements Telemetry { @@ -123,7 +124,7 @@ class PathsTelemetry implements Telemetry { sessionId?: string, threadName?: string ) { - const displayPath = toDisplayPath(path); + const displayPath = truncatePath(toDisplayPath(path)); logger.logStructuredError(`Error[${displayPath}, ${errorName}]`, { ...createCommonLogAttributes(span, projectId), path: displayPath, @@ -207,7 +208,7 @@ class PathsTelemetry implements Telemetry { flowName: featureName, sessionId, threadName, - paths: flowPaths.map((p) => toDisplayPath(p.path)), + paths: flowPaths.map((p) => truncatePath(toDisplayPath(p.path))), }); flowPaths.forEach((p) => this.writePathMetric(featureName, p)); diff --git a/js/plugins/google-cloud/src/utils.ts b/js/plugins/google-cloud/src/utils.ts index bd3f63983..3235ac898 100644 --- a/js/plugins/google-cloud/src/utils.ts +++ b/js/plugins/google-cloud/src/utils.ts @@ -18,6 +18,17 @@ import { TraceFlags } from '@opentelemetry/api'; import { ReadableSpan, TimedEvent } from '@opentelemetry/sdk-trace-base'; import { resolveCurrentPrincipal } from './auth.js'; +/** + * The maximum length (in characters) of a logged input or output. + * This limit exists to align the logs with GCP logging size limits. + * */ +const MAX_LOG_CONTENT_CHARS = 128_000; + +/** + * The maximum length (in characters) of a flow path. + */ +const MAX_PATH_CHARS = 4096; + export function extractOuterFlowNameFromPath(path: string) { if (!path || path === '') { return ''; @@ -27,6 +38,17 @@ export function extractOuterFlowNameFromPath(path: string) { return flowName ? flowName[1] : ''; } +export function truncate( + text: string, + limit: number = MAX_LOG_CONTENT_CHARS +): string { + return text ? text.substring(0, limit) : text; +} + +export function truncatePath(path: string) { + return truncate(path, MAX_PATH_CHARS); +} + /** * Extract first feature name from a path * e.g. for /{myFlow,t:flow}/{myStep,t:flowStep}/{googleai/gemini-pro,t:action,s:model} @@ -47,7 +69,7 @@ export function extractErrorName(events: TimedEvent[]): string | undefined { .map((event) => { const attributes = event.attributes; return attributes - ? (attributes['exception.type'] as string) + ? truncate(attributes['exception.type'] as string, 1024) : ''; }) .at(0); @@ -59,7 +81,7 @@ export function extractErrorMessage(events: TimedEvent[]): string | undefined { .map((event) => { const attributes = event.attributes; return attributes - ? (attributes['exception.message'] as string) + ? truncate(attributes['exception.message'] as string, 4096) : ''; }) .at(0); @@ -71,7 +93,7 @@ export function extractErrorStack(events: TimedEvent[]): string | undefined { .map((event) => { const attributes = event.attributes; return attributes - ? (attributes['exception.stacktrace'] as string) + ? truncate(attributes['exception.stacktrace'] as string, 32_768) : ''; }) .at(0); diff --git a/js/plugins/google-cloud/tests/logs_test.ts b/js/plugins/google-cloud/tests/logs_test.ts index e4540c5c1..909740712 100644 --- a/js/plugins/google-cloud/tests/logs_test.ts +++ b/js/plugins/google-cloud/tests/logs_test.ts @@ -19,6 +19,7 @@ import { beforeAll, beforeEach, describe, + expect, it, jest, } from '@jest/globals'; @@ -31,6 +32,7 @@ import { __addTransportStreamForTesting, __forceFlushSpansForTesting, __getSpanExporterForTesting, + __useJsonFormatForTesting, enableGoogleCloudTelemetry, } from '../src/index.js'; @@ -56,6 +58,207 @@ jest.mock('../src/auth.js', () => { }; }); +describe('GoogleCloudLogs with truncation', () => { + let logLines = ''; + const logStream = new Writable(); + logStream._write = (chunk, encoding, next) => { + logLines = logLines += chunk.toString(); + next(); + }; + + let ai: Genkit; + + beforeAll(async () => { + process.env.GCLOUD_PROJECT = 'test'; + process.env.GENKIT_ENV = 'dev'; + __useJsonFormatForTesting(); + __addTransportStreamForTesting(logStream); + await enableGoogleCloudTelemetry({ + projectId: 'test', + forceDevExport: false, + metricExportIntervalMillis: 100, + metricExportTimeoutMillis: 100, + }); + ai = genkit({ + // Force GCP Plugin to use in-memory metrics exporter + plugins: [], + }); + await waitForLogsInit(ai, logLines); + }); + beforeEach(async () => { + logLines = ''; + __getSpanExporterForTesting().reset(); + }); + afterAll(async () => { + await ai.stopServers(); + }); + + it('truncates large output logs', async () => { + const testModel = createModel(ai, 'testModel', async () => { + return { + message: { + role: 'user', + content: [ + { + text: 'r'.repeat(130_000), + }, + ], + }, + finishReason: 'stop', + usage: { + inputTokens: 10, + outputTokens: 14, + inputCharacters: 8, + outputCharacters: 16, + inputImages: 1, + outputImages: 3, + }, + }; + }); + const testFlow = createFlowWithInput(ai, 'testFlow', async (input) => { + return await ai.run('sub1', async () => { + return await ai.run('sub2', async () => { + return await ai.generate({ + model: testModel, + prompt: `${input} prompt`, + config: { + temperature: 1.0, + topK: 3, + topP: 5, + maxOutputTokens: 7, + }, + }); + }); + }); + }); + + await testFlow('test'); + await getExportedSpans(); + + const logMessages = await getLogs(1, 100, logLines); + const logObjects = logMessages.map((l) => JSON.parse(l as string)); + const logObjectMessages = logObjects.map( + (structuredLog) => structuredLog.message + ); + + expect(logObjectMessages).toContain('Output[testFlow, testFlow]'); + + logObjects.map((structuredLog) => { + if (structuredLog.message === 'Output[testFlow, testFlow]') { + expect(structuredLog.content.length).toBe(128_000); + } + }); + }); + + it('truncates large input logs', async () => { + const testModel = createModel(ai, 'testModel', async () => { + return { + message: { + role: 'user', + content: [ + { + text: 'response', + }, + ], + }, + finishReason: 'stop', + usage: { + inputTokens: 10, + outputTokens: 14, + inputCharacters: 8, + outputCharacters: 16, + inputImages: 1, + outputImages: 3, + }, + }; + }); + const testFlow = createFlowWithInput(ai, 'testFlow', async (input) => { + return await ai.run('sub1', async () => { + return await ai.run('sub2', async () => { + return await ai.generate({ + model: testModel, + prompt: `${input} prompt`, + config: { + temperature: 1.0, + topK: 3, + topP: 5, + maxOutputTokens: 7, + }, + }); + }); + }); + }); + + await testFlow('t'.repeat(130_000)); + await getExportedSpans(); + + const logMessages = await getLogs(1, 100, logLines); + const logObjects = logMessages.map((l) => JSON.parse(l as string)); + const logObjectMessages = logObjects.map( + (structuredLog) => structuredLog.message + ); + + expect(logObjectMessages).toContain('Input[testFlow, testFlow]'); + + logObjects.map((structuredLog) => { + if (structuredLog.message === 'Input[testFlow, testFlow]') { + expect(structuredLog.content.length).toBe(128_000); + } + }); + }); + + it.only('truncates large model names', async () => { + const testModel = createModel(ai, 'm'.repeat(2046), async () => { + return { + message: { + role: 'user', + content: [ + { + text: 'response', + }, + ], + }, + finishReason: 'stop', + usage: { + inputTokens: 10, + outputTokens: 14, + inputCharacters: 8, + outputCharacters: 16, + inputImages: 1, + outputImages: 3, + }, + }; + }); + const testFlow = createFlowWithInput(ai, 'testFlow', async (input) => { + return await ai.run('sub1', async () => { + return await ai.run('sub2', async () => { + return await ai.generate({ + model: testModel, + prompt: `${input} prompt`, + config: { + temperature: 1.0, + topK: 3, + topP: 5, + maxOutputTokens: 7, + }, + }); + }); + }); + }); + + await testFlow('test'); + await getExportedSpans(); + + const logMessages = await getLogs(1, 100, logLines); + const logObjects = logMessages.map((l) => JSON.parse(l as string)); + const logObjectModels = logObjects.map( + (structuredLog) => structuredLog.model + ); + + expect(logObjectModels).toContain('m'.repeat(1024)); + }); +}); + describe('GoogleCloudLogs', () => { let logLines = ''; const logStream = new Writable(); From af71fd7cb0227366ebce289a16df2501df9a6a8e Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Wed, 29 Jan 2025 12:33:58 -0500 Subject: [PATCH 341/562] feat: Move IDX template from idx github to Genkit github (#1681) * feat: Move IDX template from idx github to Genkit github to make it easier to maintain --- samples/js-character-generator/.gitignore | 3 ++ samples/js-character-generator/.idx/dev.nix | 38 +++++++++++++ samples/js-character-generator/README.md | 20 +++++++ samples/js-character-generator/README_IDX.md | 7 +++ .../js-character-generator/idx-template.json | 8 +++ .../js-character-generator/idx-template.nix | 16 ++++++ samples/js-character-generator/index.ts | 53 +++++++++++++++++++ samples/js-character-generator/package.json | 21 ++++++++ 8 files changed, 166 insertions(+) create mode 100644 samples/js-character-generator/.gitignore create mode 100644 samples/js-character-generator/.idx/dev.nix create mode 100644 samples/js-character-generator/README.md create mode 100644 samples/js-character-generator/README_IDX.md create mode 100644 samples/js-character-generator/idx-template.json create mode 100644 samples/js-character-generator/idx-template.nix create mode 100644 samples/js-character-generator/index.ts create mode 100644 samples/js-character-generator/package.json diff --git a/samples/js-character-generator/.gitignore b/samples/js-character-generator/.gitignore new file mode 100644 index 000000000..3e8f6dd90 --- /dev/null +++ b/samples/js-character-generator/.gitignore @@ -0,0 +1,3 @@ +test/ +node_modules/ +.genkit/ \ No newline at end of file diff --git a/samples/js-character-generator/.idx/dev.nix b/samples/js-character-generator/.idx/dev.nix new file mode 100644 index 000000000..328c569d2 --- /dev/null +++ b/samples/js-character-generator/.idx/dev.nix @@ -0,0 +1,38 @@ +# To learn more about how to use Nix to configure your environment +# see: https://developers.google.com/idx/guides/customize-idx-env +{ pkgs, ... }: { + # Which nixpkgs channel to use. + channel = "stable-24.05"; # or "unstable" + # Use https://search.nixos.org/packages to find packages + packages = [ + pkgs.nodejs_20 + pkgs.util-linux + # pkgs.go + ]; + # Sets environment variables in the workspace + env = { + #TODO Get a API key from https://g.co/ai/idxGetGeminiKey + GOOGLE_GENAI_API_KEY = "TODO"; + }; + idx = { + internal.templates-cli.enable = true; + # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" + extensions = [ + # "vscodevim.vim" + # "golang.go" + ]; + + # Workspace lifecycle hooks + workspace = { + # Runs when a workspace is first created + onCreate = { + npm-install = "npm ci --no-audit --prefer-offline --no-progress --timing"; + default.openFiles = [ "README.md" "index.ts" ]; + }; + # Runs when the workspace is (re)started + onStart = { + run-server = "npm run dev"; + }; + }; + }; +} \ No newline at end of file diff --git a/samples/js-character-generator/README.md b/samples/js-character-generator/README.md new file mode 100644 index 000000000..2f67f5068 --- /dev/null +++ b/samples/js-character-generator/README.md @@ -0,0 +1,20 @@ +# Character Generator + + + + + + Open in IDX + + + +This sample is used as the Genkit template in Project IDX, you can check it out in IDX! + +To run it locally, set a [Gemini API key](https://aistudio.google.com/app/apikey) in your environment with `export GOOGLE_GENAI_API_KEY=<>` and start the app with `npm install && npm run dev` diff --git a/samples/js-character-generator/README_IDX.md b/samples/js-character-generator/README_IDX.md new file mode 100644 index 000000000..3eed96124 --- /dev/null +++ b/samples/js-character-generator/README_IDX.md @@ -0,0 +1,7 @@ +#Genkit + +This is a simple demonstration web app using the [Firebase Genkit Library](https://github.com/firebase/genkit) with Gemini to characters for an adventure game. + +In IDX, get started by with API key at https://g.co/ai/idxGetGeminiKey and enter it in `.idx/dev.nix` and rebuild the environment. + +After rebuilding the environment, open a new terminal (`Ctrl`+ `` ` ``) and follow the link that said "Genkit Developer UI" to use Genkit's built-in local developer playground. diff --git a/samples/js-character-generator/idx-template.json b/samples/js-character-generator/idx-template.json new file mode 100644 index 000000000..4218bc312 --- /dev/null +++ b/samples/js-character-generator/idx-template.json @@ -0,0 +1,8 @@ +{ + "name": "Genkit", + "description": "Build a new web app using Firebase Genkit", + "categories": ["Web app", "AI ML"], + "icon": "https://www.gstatic.com/monospace/240513/logo_firebase.svg", + "params": [], + "publisher": "Google LLC" +} diff --git a/samples/js-character-generator/idx-template.nix b/samples/js-character-generator/idx-template.nix new file mode 100644 index 000000000..565f0ecb9 --- /dev/null +++ b/samples/js-character-generator/idx-template.nix @@ -0,0 +1,16 @@ +{pkgs, language ? "js", tos ? "false", ... }: { + packages = [ + pkgs.nodejs + ]; + bootstrap = '' + mkdir "$out" + mkdir "$out"/.idx + cp -r ${./.idx}/. "$out/.idx/" + cp -f ${./package.json} "$out/package.json" + cp -f ${./package-lock.json} "$out/package-lock.json" + cp -f ${./index.ts} "$out/index.ts" + cp -f ${./.gitignore} "$out/.gitignore" + cp ${./README_IDX.md} "$out"/README.md + chmod -R u+w "$out" + ''; +} \ No newline at end of file diff --git a/samples/js-character-generator/index.ts b/samples/js-character-generator/index.ts new file mode 100644 index 000000000..572222e06 --- /dev/null +++ b/samples/js-character-generator/index.ts @@ -0,0 +1,53 @@ +/** + * 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. + */ + +import { gemini15Flash, googleAI } from '@genkit-ai/googleai'; +import { genkit, z } from 'genkit'; + +const ai = genkit({ + plugins: [googleAI()], +}); + +const prompt = ai.definePrompt( + { + name: 'Character Prompt', + model: gemini15Flash, + input: { + schema: z.object({ + inspiration: z.string(), + }), + }, + output: { + format: 'json', + schema: z.object({ + name: z.string(), + strength: z.number(), + intelligence: z.number(), + description: z.string(), + }), + }, + }, + ` +You're a expert DnD designer, create a new character. + +Base the character on {{inspiration}} but don't make it +and exact match. +` +); + +(async () => { + console.log((await prompt({ inspiration: 'Yogi Berra' })).output); +})(); diff --git a/samples/js-character-generator/package.json b/samples/js-character-generator/package.json new file mode 100644 index 000000000..859f0cfb6 --- /dev/null +++ b/samples/js-character-generator/package.json @@ -0,0 +1,21 @@ +{ + "name": "Character Generator", + "version": "0.1.0", + "description": "A simple Firebase Genkit app to generate fantasy characters", + "main": "index.js", + "scripts": { + "dev": "genkit start -- npx tsx --watch index.ts" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "genkit-cli": "^0.9", + "tsx": "^4.19.2", + "typescript": "^5.6.3" + }, + "dependencies": { + "@genkit-ai/googleai": "^0.9", + "genkit": "^0.9" + } +} From a018679bdfbd686d98675c78bba99fda8dad63bb Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Wed, 29 Jan 2025 13:02:56 -0500 Subject: [PATCH 342/562] fix(js/ai/prompt): delete null descriptions returned by dotprompt (#1687) --- js/ai/src/prompt.ts | 6 ++++++ js/core/tests/schema_test.ts | 24 ++++++++++++++++++++++++ js/genkit/tests/prompts/schemaRef.prompt | 9 +++++++++ js/genkit/tests/prompts_test.ts | 19 +++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 js/genkit/tests/prompts/schemaRef.prompt diff --git a/js/ai/src/prompt.ts b/js/ai/src/prompt.ts index c649198f3..bdbe20ba2 100644 --- a/js/ai/src/prompt.ts +++ b/js/ai/src/prompt.ts @@ -736,6 +736,12 @@ function loadPrompt( if (variant) { promptMetadata.variant = variant; } + + // dotprompt can set null description on the schema, which can confuse downstream schema consumers + if (promptMetadata.output?.schema?.description === null) { + delete promptMetadata.output?.schema?.description; + } + return { name: registryDefinitionKey(name, variant ?? undefined, ns), model: promptMetadata.model, diff --git a/js/core/tests/schema_test.ts b/js/core/tests/schema_test.ts index faba9627b..c76abd7bb 100644 --- a/js/core/tests/schema_test.ts +++ b/js/core/tests/schema_test.ts @@ -20,6 +20,7 @@ import { describe, it } from 'node:test'; import { ValidationError, parseSchema, + toJsonSchema, validateSchema, z, } from '../src/schema.js'; @@ -138,3 +139,26 @@ describe('parse()', () => { ); }); }); + +describe('toJsonSchema', () => { + it('converts zod to JSON schema', async () => { + assert.deepStrictEqual( + toJsonSchema({ + schema: z.object({ + output: z.string(), + }), + }), + { + $schema: 'http://json-schema.org/draft-07/schema#', + additionalProperties: true, + properties: { + output: { + type: 'string', + }, + }, + required: ['output'], + type: 'object', + } + ); + }); +}); diff --git a/js/genkit/tests/prompts/schemaRef.prompt b/js/genkit/tests/prompts/schemaRef.prompt new file mode 100644 index 000000000..3c4d37f6b --- /dev/null +++ b/js/genkit/tests/prompts/schemaRef.prompt @@ -0,0 +1,9 @@ +--- +model: googleai/gemini-1.5-flash +input: + schema: myInputSchema +output: + schema: myOutputSchema +--- + +Write a poem about {{foo}}. \ No newline at end of file diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index 6a19d7f99..816d4e738 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -900,6 +900,8 @@ describe.only('prompt', () => { }); defineEchoModel(ai); pm = defineProgrammableModel(ai); + ai.defineSchema('myInputSchema', z.object({ foo: z.string() })); + ai.defineSchema('myOutputSchema', z.object({ output: z.string() })); }); it('loads from from the folder', async () => { @@ -1014,6 +1016,23 @@ describe.only('prompt', () => { }); }); + it('resolved schema refs', async () => { + const prompt = ai.prompt('schemaRef'); + + const rendered = await prompt.render({ foo: 'bar' }); + assert.deepStrictEqual(rendered.output?.jsonSchema, { + $schema: 'http://json-schema.org/draft-07/schema#', + additionalProperties: true, + properties: { + output: { + type: 'string', + }, + }, + required: ['output'], + type: 'object', + }); + }); + it('loads a varaint from from the folder', async () => { const testPrompt = ai.prompt('test', { variant: 'variant' }); // see tests/prompts folder From baa5b19917801df3b0e4282c5e90ff72c5d0720b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:09:06 +0000 Subject: [PATCH 343/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.9 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 6bc948db4..6a22abad2 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From f728b3de77e2de595b972ccfc528dda4df5c76b6 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:09:09 +0000 Subject: [PATCH 344/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.9 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index c761bd3ef..555eb52d0 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From 60df150908e19edd041e47af2f6ae7476655debf Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:09:11 +0000 Subject: [PATCH 345/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.9 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index c7e53c2fc..0f7ea6caf 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From df6a11e88ff3133ce0786d7df5b34fc10dac2a02 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:11:21 +0000 Subject: [PATCH 346/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.9 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 5be601f94..67521cc47 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From 81fe946a419a41b5552c1260f7897ac2673298e3 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:11:24 +0000 Subject: [PATCH 347/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.9 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index 6dcff73b7..8e454c5a0 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From d0ffe2807879394b5736eb8f557a0687bfad63e9 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:11:29 +0000 Subject: [PATCH 348/562] chore: bump genkit version to genkit@1.0.0-rc.9 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index bb6df6955..965050290 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From 1994cb14931d8e81706f23642cc5a15b82e9fd5b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:11:33 +0000 Subject: [PATCH 349/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.9 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index 61b42e09d..37e33d301 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From 531a2b5a3a2e97732364d274f5855d7d9d086275 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:11:35 +0000 Subject: [PATCH 350/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.9 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 782eda705..ef0b37b8f 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From 3e20e7d9c985dacbbe70b9bd2566f6dbc1e9af35 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:11:38 +0000 Subject: [PATCH 351/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.9 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 0427f1c05..02ab8efa4 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From 0cd9b52d93b34a214a6120db5b1ff750ce72cf4c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:11:41 +0000 Subject: [PATCH 352/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.9 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 0645a8170..03646e134 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From a384abad50083a8cc15babf9963a5efca27eb8a1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:11:44 +0000 Subject: [PATCH 353/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.9 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index b8d0c0db8..6cc023183 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From ce2b547a2c6ab31fe6ac85dae2e5f511be42b21c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:11:48 +0000 Subject: [PATCH 354/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.9 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 7d022e53b..8dc50bfa5 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From 7fa79b561275e8addc6eec3197e563df1c9d433b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:11:50 +0000 Subject: [PATCH 355/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.9 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index df5d8e85a..5ac2bc2b2 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From a5a06b7f5b06a949988e223bb28fbe20386f7a89 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:11:54 +0000 Subject: [PATCH 356/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.9 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index b7787094d..617c5b442 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From c2614bc9a9892ae97399bee53ab0e5b50dae0267 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:11:57 +0000 Subject: [PATCH 357/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.9 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index a1fac3596..b252d87b2 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From 795519e0e58a1697c39cb011fa32b26989134feb Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:12:00 +0000 Subject: [PATCH 358/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.9 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 5a3abae5a..14169f369 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From 2234edd4c2ac66e237103e83127f8f07229858b1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:12:03 +0000 Subject: [PATCH 359/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.9 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index 234ca49dd..36d28b399 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From b07bc30d16789afe1f05ec647074692237c3d69d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:12:06 +0000 Subject: [PATCH 360/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.9 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index b10dace2d..00628c507 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From 58bf7bd992a27b59757e7d78d1cc2efb5a112846 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 18:12:10 +0000 Subject: [PATCH 361/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.9 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index 053690a58..9cb879a4d 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.8", + "version": "1.0.0-rc.9", "type": "commonjs", "scripts": { "check": "tsc", From 1bded3c7e3e73d4e877ea9d2ad70b8f004fc55d5 Mon Sep 17 00:00:00 2001 From: Yesudeep Mangalapilly Date: Wed, 29 Jan 2025 10:45:14 -0800 Subject: [PATCH 362/562] feat(py): initial workspace for python implementation (#1682) * feat: bootstrap python workspace Changelog: - [ ] Setup scripts - [ ] Workspace, packaging, and dependency management using uv - [ ] Initial internal engineering documentation using mkdocs - [ ] Basic smoke tests - [ ] Basic hello world sample that works with Vertex AI and the genkit plugin --------- Co-authored-by: Pavel Jbanov --- .prettierignore | 1 + genkit-tools/common/src/utils/utils.ts | 25 +- py/.gitignore | 32 + py/.hooks/commit-message-format-pre-push | 19 + py/.hooks/no-commits-on-branches | 13 + py/.projectile | 0 py/.python-version | 1 + py/LICENSE | 201 ++ py/README.md | 19 + py/SUMMARY.md | 10 + py/bin/build_dists | 46 + py/bin/fmt | 55 + py/bin/format_toml_files | 71 + py/bin/setup | 201 ++ py/captainhook.json | 91 + py/engdoc/assets/favicon.png | Bin 0 -> 668 bytes py/engdoc/assets/logo.png | Bin 0 -> 1022 bytes py/engdoc/contributing/git_workflow.md | 0 py/engdoc/contributing/index.md | 189 ++ py/engdoc/contributing/installation.md | 0 py/engdoc/contributing/troubleshooting.md | 0 py/engdoc/extending/glossary.md | 14 + py/engdoc/extending/go/index.md | 1 + py/engdoc/extending/index.md | 254 ++ py/engdoc/extending/python/api.md | 3 + py/engdoc/extending/python/getting_started.md | 1 + py/engdoc/extending/python/index.md | 212 ++ py/engdoc/extending/tooling/index.md | 1 + py/engdoc/extending/typescript/index.md | 1 + py/engdoc/img/asgi.svg | 4 + py/engdoc/img/onion.svg | 4 + py/engdoc/index.md | 571 ++++ py/engdoc/user_guide/developer_tools.md | 0 py/engdoc/user_guide/introduction.md | 0 py/mkdocs.yml | 73 + py/packages/dotprompt/LICENSE | 201 ++ py/packages/dotprompt/README.md | 0 py/packages/dotprompt/pyproject.toml | 30 + .../dotprompt/src/dotprompt/__init__.py | 2 + py/packages/dotprompt/src/dotprompt/py.typed | 0 py/packages/genkit/LICENSE | 201 ++ py/packages/genkit/README.md | 0 py/packages/genkit/pyproject.toml | 35 + py/packages/genkit/src/genkit/ai/__init__.py | 24 + py/packages/genkit/src/genkit/ai/model.py | 18 + py/packages/genkit/src/genkit/ai/prompt.py | 18 + .../genkit/src/genkit/core/__init__.py | 24 + py/packages/genkit/src/genkit/core/action.py | 105 + .../genkit/src/genkit/core/reflection.py | 106 + .../genkit/src/genkit/core/registry.py | 34 + py/packages/genkit/src/genkit/core/tracing.py | 115 + py/packages/genkit/src/genkit/core/types.py | 463 ++++ .../genkit/src/genkit/plugins/.gitignore | 0 py/packages/genkit/src/genkit/py.typed | 0 .../genkit/src/genkit/veneer/__init__.py | 151 ++ py/plugins/chroma/LICENSE | 201 ++ py/plugins/chroma/README.md | 3 + py/plugins/chroma/pyproject.toml | 30 + .../src/genkit/plugins/chroma/__init__.py | 24 + py/plugins/chroma/src/genkit/py.typed | 0 py/plugins/firebase/LICENSE | 201 ++ py/plugins/firebase/README.md | 4 + py/plugins/firebase/pyproject.toml | 30 + .../src/genkit/plugins/firebase/__init__.py | 24 + py/plugins/firebase/src/genkit/py.typed | 0 py/plugins/google-ai/LICENSE | 201 ++ py/plugins/google-ai/README.md | 4 + py/plugins/google-ai/pyproject.toml | 30 + .../src/genkit/plugins/google_ai/__init__.py | 24 + .../plugins/google_ai/models/__init__.py | 24 + py/plugins/google-ai/src/genkit/py.typed | 0 py/plugins/google-cloud/LICENSE | 201 ++ py/plugins/google-cloud/README.md | 4 + py/plugins/google-cloud/pyproject.toml | 30 + .../genkit/plugins/google_cloud/__init__.py | 24 + py/plugins/google-cloud/src/genkit/py.typed | 0 py/plugins/ollama/LICENSE | 201 ++ py/plugins/ollama/README.md | 3 + py/plugins/ollama/pyproject.toml | 30 + .../src/genkit/plugins/ollama/__init__.py | 24 + .../genkit/plugins/ollama/models/__init__.py | 24 + py/plugins/ollama/src/genkit/py.typed | 0 py/plugins/pinecone/LICENSE | 201 ++ py/plugins/pinecone/README.md | 4 + py/plugins/pinecone/pyproject.toml | 30 + .../src/genkit/plugins/pinecone/__init__.py | 24 + py/plugins/pinecone/src/genkit/py.typed | 0 py/plugins/vertex-ai/LICENSE | 201 ++ py/plugins/vertex-ai/README.md | 4 + py/plugins/vertex-ai/pyproject.toml | 30 + .../src/genkit/plugins/vertex_ai/__init__.py | 77 + .../plugins/vertex_ai/models/__init__.py | 24 + py/plugins/vertex-ai/src/genkit/py.typed | 0 py/pyproject.toml | 80 + py/samples/.gitignore | 0 py/samples/hello/LICENSE | 201 ++ py/samples/hello/README.md | 18 + py/samples/hello/hello.py | 66 + py/samples/hello/pyproject.toml | 39 + py/taplo.toml | 5 + py/tests/smoke/LICENSE | 201 ++ py/tests/smoke/README.md | 4 + py/tests/smoke/package_test.py | 44 + py/tests/smoke/pyproject.toml | 37 + py/uv.lock | 2397 +++++++++++++++++ 105 files changed, 8666 insertions(+), 2 deletions(-) create mode 100644 py/.gitignore create mode 100755 py/.hooks/commit-message-format-pre-push create mode 100755 py/.hooks/no-commits-on-branches create mode 100644 py/.projectile create mode 100644 py/.python-version create mode 100644 py/LICENSE create mode 100644 py/README.md create mode 100644 py/SUMMARY.md create mode 100755 py/bin/build_dists create mode 100755 py/bin/fmt create mode 100755 py/bin/format_toml_files create mode 100755 py/bin/setup create mode 100644 py/captainhook.json create mode 100644 py/engdoc/assets/favicon.png create mode 100644 py/engdoc/assets/logo.png create mode 100644 py/engdoc/contributing/git_workflow.md create mode 100644 py/engdoc/contributing/index.md create mode 100644 py/engdoc/contributing/installation.md create mode 100644 py/engdoc/contributing/troubleshooting.md create mode 100644 py/engdoc/extending/glossary.md create mode 100644 py/engdoc/extending/go/index.md create mode 100644 py/engdoc/extending/index.md create mode 100644 py/engdoc/extending/python/api.md create mode 100644 py/engdoc/extending/python/getting_started.md create mode 100644 py/engdoc/extending/python/index.md create mode 100644 py/engdoc/extending/tooling/index.md create mode 100644 py/engdoc/extending/typescript/index.md create mode 100644 py/engdoc/img/asgi.svg create mode 100644 py/engdoc/img/onion.svg create mode 100644 py/engdoc/index.md create mode 100644 py/engdoc/user_guide/developer_tools.md create mode 100644 py/engdoc/user_guide/introduction.md create mode 100644 py/mkdocs.yml create mode 100644 py/packages/dotprompt/LICENSE create mode 100644 py/packages/dotprompt/README.md create mode 100644 py/packages/dotprompt/pyproject.toml create mode 100644 py/packages/dotprompt/src/dotprompt/__init__.py create mode 100644 py/packages/dotprompt/src/dotprompt/py.typed create mode 100644 py/packages/genkit/LICENSE create mode 100644 py/packages/genkit/README.md create mode 100644 py/packages/genkit/pyproject.toml create mode 100644 py/packages/genkit/src/genkit/ai/__init__.py create mode 100644 py/packages/genkit/src/genkit/ai/model.py create mode 100644 py/packages/genkit/src/genkit/ai/prompt.py create mode 100644 py/packages/genkit/src/genkit/core/__init__.py create mode 100644 py/packages/genkit/src/genkit/core/action.py create mode 100644 py/packages/genkit/src/genkit/core/reflection.py create mode 100644 py/packages/genkit/src/genkit/core/registry.py create mode 100644 py/packages/genkit/src/genkit/core/tracing.py create mode 100644 py/packages/genkit/src/genkit/core/types.py create mode 100644 py/packages/genkit/src/genkit/plugins/.gitignore create mode 100644 py/packages/genkit/src/genkit/py.typed create mode 100644 py/packages/genkit/src/genkit/veneer/__init__.py create mode 100644 py/plugins/chroma/LICENSE create mode 100644 py/plugins/chroma/README.md create mode 100644 py/plugins/chroma/pyproject.toml create mode 100644 py/plugins/chroma/src/genkit/plugins/chroma/__init__.py create mode 100644 py/plugins/chroma/src/genkit/py.typed create mode 100644 py/plugins/firebase/LICENSE create mode 100644 py/plugins/firebase/README.md create mode 100644 py/plugins/firebase/pyproject.toml create mode 100644 py/plugins/firebase/src/genkit/plugins/firebase/__init__.py create mode 100644 py/plugins/firebase/src/genkit/py.typed create mode 100644 py/plugins/google-ai/LICENSE create mode 100644 py/plugins/google-ai/README.md create mode 100644 py/plugins/google-ai/pyproject.toml create mode 100644 py/plugins/google-ai/src/genkit/plugins/google_ai/__init__.py create mode 100644 py/plugins/google-ai/src/genkit/plugins/google_ai/models/__init__.py create mode 100644 py/plugins/google-ai/src/genkit/py.typed create mode 100644 py/plugins/google-cloud/LICENSE create mode 100644 py/plugins/google-cloud/README.md create mode 100644 py/plugins/google-cloud/pyproject.toml create mode 100644 py/plugins/google-cloud/src/genkit/plugins/google_cloud/__init__.py create mode 100644 py/plugins/google-cloud/src/genkit/py.typed create mode 100644 py/plugins/ollama/LICENSE create mode 100644 py/plugins/ollama/README.md create mode 100644 py/plugins/ollama/pyproject.toml create mode 100644 py/plugins/ollama/src/genkit/plugins/ollama/__init__.py create mode 100644 py/plugins/ollama/src/genkit/plugins/ollama/models/__init__.py create mode 100644 py/plugins/ollama/src/genkit/py.typed create mode 100644 py/plugins/pinecone/LICENSE create mode 100644 py/plugins/pinecone/README.md create mode 100644 py/plugins/pinecone/pyproject.toml create mode 100644 py/plugins/pinecone/src/genkit/plugins/pinecone/__init__.py create mode 100644 py/plugins/pinecone/src/genkit/py.typed create mode 100644 py/plugins/vertex-ai/LICENSE create mode 100644 py/plugins/vertex-ai/README.md create mode 100644 py/plugins/vertex-ai/pyproject.toml create mode 100644 py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/__init__.py create mode 100644 py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/models/__init__.py create mode 100644 py/plugins/vertex-ai/src/genkit/py.typed create mode 100644 py/pyproject.toml create mode 100644 py/samples/.gitignore create mode 100644 py/samples/hello/LICENSE create mode 100644 py/samples/hello/README.md create mode 100644 py/samples/hello/hello.py create mode 100644 py/samples/hello/pyproject.toml create mode 100644 py/taplo.toml create mode 100644 py/tests/smoke/LICENSE create mode 100644 py/tests/smoke/README.md create mode 100644 py/tests/smoke/package_test.py create mode 100644 py/tests/smoke/pyproject.toml create mode 100644 py/uv.lock diff --git a/.prettierignore b/.prettierignore index abe0c58a6..86e82a2ee 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,3 +6,4 @@ pnpm-lock.yaml docs/* docs-go/* genkit-tools/genkit-schema.json +py/* diff --git a/genkit-tools/common/src/utils/utils.ts b/genkit-tools/common/src/utils/utils.ts index 0973c6b88..a117714f1 100644 --- a/genkit-tools/common/src/utils/utils.ts +++ b/genkit-tools/common/src/utils/utils.ts @@ -26,8 +26,16 @@ export async function findProjectRoot(): Promise { while (currentDir !== path.parse(currentDir).root) { const packageJsonPath = path.join(currentDir, 'package.json'); const goModPath = path.join(currentDir, 'go.mod'); + const pyprojectPath = path.join(currentDir, 'pyproject.toml'); + const pyproject2Path = path.join(currentDir, 'requirements.txt'); + try { - const [packageJsonExists, goModExists] = await Promise.all([ + const [ + packageJsonExists, + goModExists, + pyprojectExists, + pyproject2Exists, + ] = await Promise.all([ fs .access(packageJsonPath) .then(() => true) @@ -36,8 +44,21 @@ export async function findProjectRoot(): Promise { .access(goModPath) .then(() => true) .catch(() => false), + fs + .access(pyprojectPath) + .then(() => true) + .catch(() => false), + fs + .access(pyproject2Path) + .then(() => true) + .catch(() => false), ]); - if (packageJsonExists || goModExists) { + if ( + packageJsonExists || + goModExists || + pyprojectExists || + pyproject2Exists + ) { return currentDir; } } catch { diff --git a/py/.gitignore b/py/.gitignore new file mode 100644 index 000000000..047135ac9 --- /dev/null +++ b/py/.gitignore @@ -0,0 +1,32 @@ +*$py.class +*.egg +*.egg-info/ +*.py[cod] +*.so +.DS_Store +.Python +.cache/ +.coverage +.eggs/ +.genkit +.idea/ +.installed.cfg +.mypy_cache/ +.pytest_cache/ +.ruff_cache/ +.venv +ENV/ +__pycache__/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +site/ +var/ +venv/ +wheels/ diff --git a/py/.hooks/commit-message-format-pre-push b/py/.hooks/commit-message-format-pre-push new file mode 100755 index 000000000..4be89de7c --- /dev/null +++ b/py/.hooks/commit-message-format-pre-push @@ -0,0 +1,19 @@ +#!/usr/bin/env sh +# put this file in: .git/hooks/pre-push +z40=0000000000000000000000000000000000000000 +while read _ local_sha _ remote_sha; do + if [ "$local_sha" != $z40 ]; then + if [ "$remote_sha" = $z40 ]; then + # New branch, examine all commits + range="$local_sha" + else + # Update to existing branch, examine new commits + range="$remote_sha..$local_sha" + fi + + # Check for WIP commit + if ! convco check "$range"; then + exit 1 + fi + fi +done diff --git a/py/.hooks/no-commits-on-branches b/py/.hooks/no-commits-on-branches new file mode 100755 index 000000000..45824624c --- /dev/null +++ b/py/.hooks/no-commits-on-branches @@ -0,0 +1,13 @@ +#!/bin/bash +# pre-commit script + +protected_branches="$@" + +branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,') + +for protected_branch in $protected_branches; do + if [ "$branch" = "$protected_branch" ]; then + echo "Direct commits to the '$protected_branch' branch are not allowed." + exit 1 + fi +done diff --git a/py/.projectile b/py/.projectile new file mode 100644 index 000000000..e69de29bb diff --git a/py/.python-version b/py/.python-version new file mode 100644 index 000000000..e4fba2183 --- /dev/null +++ b/py/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/py/LICENSE b/py/LICENSE new file mode 100644 index 000000000..220539673 --- /dev/null +++ b/py/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/py/README.md b/py/README.md new file mode 100644 index 000000000..70e1fcd72 --- /dev/null +++ b/py/README.md @@ -0,0 +1,19 @@ +# Firebase Genkit Python SDK + +TODO + +## Install prerequisites + +```bash +bin/setup +``` + +## Run all unit tests + +``` bash +uv run pytest . +``` + +## Run test app + +See the README.md in samples/hello. diff --git a/py/SUMMARY.md b/py/SUMMARY.md new file mode 100644 index 000000000..6c3cbb125 --- /dev/null +++ b/py/SUMMARY.md @@ -0,0 +1,10 @@ +# Summary + +* [Introduction](docs/introduction.md) + * [Get Started](docs/get_started.md) +* [Engineering](docs/eng/index.md) + * [Python](docs/eng/py/index.md) + * [Getting Started](docs/eng/py/getting_started.md) + * [Go](docs/eng/go/index.md) + * [TypeScript](docs/eng/ts/index.md) +* [License](LICENSE) diff --git a/py/bin/build_dists b/py/bin/build_dists new file mode 100755 index 000000000..3eb18635c --- /dev/null +++ b/py/bin/build_dists @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# +# Build distributions for all projects +# +# 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. + +set -euo pipefail + +if ((EUID == 0)); then + echo "Please do not run as root" + exit +fi + +TOP_DIR=$(git rev-parse --show-toplevel) + +PROJECT_DIRS=( + "packages/dotprompt" + "packages/genkit" + "plugins/chroma" + "plugins/firebase" + "plugins/google-ai" + "plugins/google-cloud" + "plugins/ollama" + "plugins/pinecone" + "plugins/vertex-ai" + "samples/hello" +) + +for PROJECT_DIR in "${PROJECT_DIRS[@]}"; do + uv \ + --directory=${TOP_DIR}/py \ + --project "$PROJECT_DIR" \ + build +done diff --git a/py/bin/fmt b/py/bin/fmt new file mode 100755 index 000000000..327cfd5ea --- /dev/null +++ b/py/bin/fmt @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# +# Format all files in the project +# +# 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. + +#set -x # Uncomment to enable tracing. +set -euo pipefail + +if ((EUID == 0)); then + echo "Please do not run as root" + exit +fi + +TOP_DIR=$(git rev-parse --show-toplevel) + +# Format all TOML files. +"${TOP_DIR}/py/bin/format_toml_files" +if [[ $? -ne 0 ]]; then + exit 1 +fi + +# Format all Python code. +uvx ruff format "${TOP_DIR}/py" +if [[ $? -ne 0 ]]; then + exit 1 +fi + +# Format all Go code. +pushd ${TOP_DIR} +go fmt go/... +if [[ $? -ne 0 ]]; then + exit 1 +fi +popd + +# Format all TypeScript code. +pushd ${TOP_DIR} +pnpm run format +if [[ $? -ne 0 ]]; then + exit 1 +fi +popd diff --git a/py/bin/format_toml_files b/py/bin/format_toml_files new file mode 100755 index 000000000..3b5ed705c --- /dev/null +++ b/py/bin/format_toml_files @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# +# Format all TOML files in the project. +# +# 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. + +set -euo pipefail + +GIT_ROOT=$(git rev-parse --show-toplevel) + +if command -v rust-parallel >/dev/null 2>&1; then + if command -v fd >/dev/null 2>&1; then + fd -e toml \ + --exclude 'py/**/*.egg-info/**' \ + --exclude 'py/**/.dist/**' \ + --exclude 'py/**/.next/**' \ + --exclude 'py/**/.output/**' \ + --exclude 'py/**/.pytest_cache/**' \ + --exclude 'py/**/.venv/**' \ + --exclude 'py/**/__pycache__/**' \ + --exclude 'py/**/build/**' \ + --exclude 'py/**/develop-eggs/**' \ + --exclude 'py/**/dist/**' \ + --exclude 'py/**/eggs/**' \ + --exclude 'py/**/node_modules/**' \ + --exclude 'py/**/sdist/**' \ + --exclude 'py/**/site/**' \ + --exclude 'py/**/target/**' \ + --exclude 'py/**/venv/**' \ + --exclude 'py/**/wheels/**' | + rust-parallel -j4 \ + taplo format --config "${GIT_ROOT}/py/taplo.toml" + else + echo "Using find" + find "${GIT_ROOT}" -name "*.toml" \ + ! -path 'py/**/*.egg-info/**' \ + ! -path 'py/**/.dist/**' \ + ! -path 'py/**/.next/**' \ + ! -path 'py/**/.output/**' \ + ! -path 'py/**/.pytest_cache/**' \ + ! -path 'py/**/.venv/**' \ + ! -path 'py/**/__pycache__/**' \ + ! -path 'py/**/build/**' \ + ! -path 'py/**/develop-eggs/**' \ + ! -path 'py/**/dist/**' \ + ! -path 'py/**/eggs/**' \ + ! -path 'py/**/node_modules/**' \ + ! -path 'py/**/sdist/**' \ + ! -path 'py/**/site/**' \ + ! -path 'py/**/target/**' \ + ! -path 'py/**/venv/**' \ + ! -path 'py/**/wheels/**' \ + -print0 | + rust-parallel -j4 \ + taplo format --config "${GIT_ROOT}/py/taplo.toml" + fi +else + echo "Please install GNU parallel to use this script" +fi diff --git a/py/bin/setup b/py/bin/setup new file mode 100755 index 000000000..f264ed8cc --- /dev/null +++ b/py/bin/setup @@ -0,0 +1,201 @@ +#!/usr/bin/env bash +# +# Setup script for Genkit engineering. +# +# 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. + +if ((EUID == 0)) && [[ -z ${DANGEROUSLY_RUN_AS_ROOT+x} ]]; then + echo "Please do not run as root unless DANGEROUSLY_RUN_AS_ROOT is set." + exit 1 +fi + +[[ ${EUID} != 0 ]] && set -euo pipefail + +TOP_DIR=$(git rev-parse --show-toplevel) + +AUDIENCE="eng" +while getopts ":a:" opt; do + case ${opt} in + a) + AUDIENCE="${OPTARG}" + ;; + \?) + echo "Invalid option: -${OPTARG}" >&2 + exit 1 + ;; + *) + echo "Unsupported option: -${OPTARG}" >&2 + exit 1 + ;; + esac +done + +if [[ -z ${AUDIENCE} ]]; then + echo "Audience flag (-a) is required." + echo "Usage: $0 -a " + exit 1 +fi + +OS_NAME=$(uname) + +# Install all the required tools common to all audiences. +function genkit::install_prerequisites() { + if [[ ${OS_NAME} == "Darwin" && -x "$(command -v brew)" ]]; then + # Darwin-based systems. + brew install \ + curl \ + gh \ + go \ + node \ + python3 + elif [[ -x "$(command -v apt)" ]]; then + # Debian-based systems. + sudo apt install -y \ + curl \ + gh \ + golang \ + nodejs \ + python3 + elif [[ -x "$(command -v dnf)" ]]; then + # Fedora-based systems. + sudo dnf install -y \ + curl \ + gh \ + go \ + node \ + python3 + else + echo "Unsupported OS. Please install protoc manually." + fi + + # Install rust. + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + ~/.cargo/bin/rustup update + + # Install uv for Python versioning, packaging, and workspace management. + curl -LsSf https://astral.sh/uv/install.sh | sh + + # Install pnpm for JavaScript package management. + curl -fsSL https://get.pnpm.io/install.sh | env PNPM_VERSION=10.0.0 sh - +} + +function genkit::install_google_cloud_sdk() { + # This depends on Python 3.11 and installs it for the user on some systems. + if command -v gcloud &>/dev/null; then + gcloud config set disable_usage_reporting true + gcloud components update + return 0 + fi + + curl https://sdk.cloud.google.com | bash -s -- --disable-prompts + gcloud config set disable_usage_reporting true +} + +# Install all the required tools for CI. +function genkit::install_ci_packages() { + genkit::install_prerequisites + genkit::install_python_cli_tools + genkit::install_docs_cli_tools + genkit::install_pnpm_cli_tools +} + +# Install all the required tools for engineering. +function genkit::install_eng_packages() { + genkit::install_prerequisites + genkit::install_go_cli_tools + genkit::install_cargo_cli_tools + genkit::install_python_cli_tools + genkit::install_docs_cli_tools + genkit::install_google_cloud_sdk + genkit::install_pnpm_cli_tools + genkit::install_pre_commit_hooks +} + +# Install all the required tools that have been written in Go. +function genkit::install_go_cli_tools() { + go install github.com/captainhook-go/captainhook/cmd/captainhook@latest + go install github.com/jesseduffield/lazygit@latest +} + +# Install all the required tools that have been written in Rust. We're assuming +# that the user has already installed rust and cargo. +function genkit::install_cargo_cli_tools() { + "$HOME/.cargo/bin/cargo" install --locked \ + convco \ + fd-find \ + ripgrep \ + rust-parallel \ + taplo-cli +} + +# Install NPM packages. +function genkit::install_pnpm_cli_tools() { + # Install the Genkit CLI. See: https://firebase.google.com/docs/genkit/devtools + pnpm add -g genkit-cli +} + +PYTHON_CLI_TOOLS=( + "httpie" # HTTP client. See: https://httpie.io/ + "mypy" # Static type checker. See: https://mypy.readthedocs.io/en/stable/ + "ruff" # Fast linter. See: https://github.com/astral-sh/ruff +) + +# Install all the Python-related formatter and static analysis tools. +function genkit::install_python_cli_tools() { + for package in "${PYTHON_CLI_TOOLS[@]}"; do + uv tool install "${package}" + done +} + +# Install documentation site generator. +function genkit::install_docs_cli_tools() { + # Engineering documentation site generator. + # See: https://squidfunk.github.io/mkdocs-material/ + uv tool install \ + mkdocs \ + --with mkdocs-autorefs \ + --with mkdocs-d2-plugin \ + --with mkdocs-literate-nav \ + --with mkdocs-material \ + --with mkdocs-mermaid2-plugin \ + --with mkdocs-minify-plugin \ + --with mkdocstrings[python] +} + +# Install pre-commit hooks. +function genkit::install_pre_commit_hooks() { + ~/go/bin/captainhook install -f -c ${TOP_DIR}/py/captainhook.json +} + +# Entry point for the setup script. +function genkit::main() { + case "${AUDIENCE}" in + eng) + genkit::install_eng_packages + ;; + ci) + genkit::install_ci_packages + ;; + *) + echo "Unsupported audience: ${AUDIENCE}" + exit 1 + ;; + esac + + echo "Please restart your shell." +} + +# Let's go! +genkit::main diff --git a/py/captainhook.json b/py/captainhook.json new file mode 100644 index 000000000..4bce26572 --- /dev/null +++ b/py/captainhook.json @@ -0,0 +1,91 @@ +{ + "config": { + "git-directory": ".git" + }, + "hooks": { + "commit-msg": { + "actions": [ + { + "run": "convco check -n 1" + } + ] + }, + "pre-commit": { + "actions": [ + { + "run": "py/.hooks/no-commits-on-branches main py" + }, + { + "run": "CaptainHook::File.MaxSize", + "config": { + "allow-failure": true + }, + "options": { + "max-size": "1M" + } + }, + { + "run": "CaptainHook::File.BlockSecrets", + "options": { + "presets": ["Aws", "GitHub", "Stripe", "Google"] + } + }, + { + "run": "pnpm i --frozen-lockfile" + }, + { + "run": "py/bin/fmt" + }, + { + "run": "uvx --directory py ruff check --fix ." + }, + { + "run": "uv run --directory py pytest ." + }, + { + "run": "uv run --directory py mkdocs build" + }, + { + "run": "py/bin/build_dists" + } + ] + }, + "pre-push": { + "actions": [ + { + "run": "CaptainHook::Branch.PreventPushOfFixupAndSquashCommits", + "options": { + "branches-to-protect": ["main"] + } + }, + { + "run": "CaptainHook::Branch.EnsureNaming", + "options": { + "regex": "^main|[a-z_][0-9a-z_]+-(build|chore|ci|docs|feat|fix|perf|refactor|revert|release|style|test)-[a-z][0-9a-z_]+-?([A-Za-z0-9a_-])*$" + } + }, + { + "run": "pnpm i --frozen-lockfile" + }, + { + "run": "py/bin/fmt" + }, + { + "run": "uvx --directory py ruff check --fix ." + }, + { + "run": "uv run --directory py pytest ." + }, + { + "run": "uv run --directory py mkdocs build" + }, + { + "run": "py/bin/build_dists" + }, + { + "run": "py/.hooks/commit-message-format-pre-push" + } + ] + } + } +} diff --git a/py/engdoc/assets/favicon.png b/py/engdoc/assets/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..c64e8bd0a574a6dd390c2d98712b343b2eb02b6e GIT binary patch literal 668 zcmV;N0%QG&P)ykWchS1XyDoOIat)O){xR2^d! zq6{jmyB~{;2zAVA#2D0gLvUhEXs=w2LR4xYcrto51|t+12c)I3pH3*YMFnPzMH2)& z`dD;zDz{GIJ55K6V1*7_b}D*HaJQ&n(uJcyaIRjBzVRgjTnNgYL4W)}P=Fg%Sb;#s zq(QW`K$so~RCFrz^g-Y+t*uI+#=n~2K47tEkOxY`&!I5Ch%yOWPk8X0BDjyNRaSll zYxrewTmH|+-%p*vOl2VZAi$o+qLf(qO_ZR*|BeED6&POtE=`C0x&fF{38Na@#RRTm zuuZX*HIU*3;*sZrziXjb;?G@?vp{7>T?%Zn3M4Qe^2?fvC0d9Gm|Y6&N?d_0f5(9t z)r!u;ygm4h}+Fg;r67K?+V00}&Y=`71@Mknw zU{`Pn2i*#s0ppA8r|^F=Bn2<1Rw66tO>nntwLc60CJ0a9-=k>c6L|_2q$&JyD{uia zUuKo~F+fZ}ufl$ahKd$@oWgPID&Dsd5|DLTR1z1sfqXQD*B&JffU((N<}zRckKxr& z8WN~RMSnd>{GNq>My=Ne(e3zeNdWLgqB#})b}4We5?=`U_ed~HMYmJ9`!EJY;7>@R zBEJHiUHL&swzng#LjKSWlEXB%>B5l{fe62fEF?0FpTP^h(dl7i`%GZ38{&ddjoJuC z_)eswqUyvrJ|ElZ_av<$ZaaYN`9O9q5UVl_H2?tQ6Yz@f(0000mMu8GYk!E_~+sKJYBwzQUHIseqeG>X+GRBPEt5#)tt z(jtX)Sras2f)njU%vxLPucn0EsP=Slc>&(<^={C4hvD+-0VI4Fw)#ZC)^(H~gB@>>e0l|f>@7e^6yWA>lV?f&n@H{HQ3Le0VvyrT@!=O3xG zyFz`tQRbvd8KK|0WLo6ASVBc@GA%|1$QH*_f{{F6!!~sS8E@t=ukclt7tCc@-c05^ z=3&A?4=0TdIOOGeN(3H^qGcloqytDj>H7kyeGyV?rm+#DzuwOaIJ?Sg)4;e2W zup^nD5v6t&+m*2@Zc+ahPCP9hu-QceqO5H%kGiwCT6Qi&+}vstVuKz3vB zfD7})1C~5U0ivXPNYfm!OC=yrJYbTY$%taH5Y}GLIn5E3JqMhdD>nYGR4zw^VM7n) z`IKl5X!Kha7|`~dSiov0o6yVX1QM_B> "${HOME}/.ssh/authorized_keys" + cat ~/.ssh/id_ed25519.pub + ``` + + Save this to a file called `gen_ssh_key.sh` and run it as follows after + changing to its container directory: + + ```bash + chmod a+x gen_ssh_key.sh + ./gen_ssh_key.sh + ``` + +5. [Associate your SSH key with your GitHub account](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account). + +6. If you're a Googler, also [associate your `@google.com` email + address](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-email-preferences/adding-an-email-address-to-your-github-account) + with your GitHub account and follow any other requirements to complete this + process. + +### GitHub Organization Membership + +Please talk to the Genkit Dec team (on [discord](https://discord.gg/qXt5zzQKpc)) +to get yourself added to the appropriate groups for your GitHub organization +membership. + +### CLA + +* Ensure you have [signed the +CLA](https://github.com/firebase/genkit/blob/main/CONTRIBUTING.md#sign-our-contributor-license-agreement). + +* For corporate CLAs, please ask the appropriate channels for help. + +## Preparing your workstation + +!!! note + + === "macOS" + + Install [Homebrew](https://brew.sh/) before proceeding. + + === "Debian" + + Your system should have the required software ready to go. + + === "Fedora" + + Your system should have the required software ready to go. + +### Install the GitHub CLI tool + +=== "macOS" + + We're assuming you have [Homebrew](https://brew.sh/) installed + already. + + + ```bash + brew install gh + ``` + +=== "Debian/Ubuntu" + + ```bash + sudo apt install gh + ``` + +=== "Fedora" + + ```bash + sudo dnf install gh + ``` + +### Authenticate the CLI with GitHub + +```bash +gh auth login +``` + +### Check out project-related repositories + +Consider setting up your workspace to mimic the paths found on GitHub for easier +disambiguation of forks: + +```bash +mkdir -p $HOME/code/github.com/firebase/ +cd $HOME/code/github.com/firebase + +gh repo clone https://github.com/firebase/genkit.git +``` + +This should allow you to produce a directory structure similar to the following: + +```bash +zsh❯ tree -L 3 code + code + └── github.com + ├── firebase + │   └── genkit + ├── google + │   └── dotprompt + └── yesudeep + ├── dotprompt + └── genkit +``` + +### Configure Git with your legal name and email address. + +!!! note inline end + + Googlers should use their `@google.com` email address + to make commits. + +```bash +git config user.email "{username}@domain.com" +git config user.Name "Your Legal Name." +``` + + +### Engineering workstations + +Run the following command from the `code/github.com/firebase/genkit` repository +working tree and it should install the required tools for you. + +```bash +py/bin/setup +``` + +### CI/CD + +The following is the equivalent used for CI/CD systems. + +```bash +py/bin/setup -e ci +``` diff --git a/py/engdoc/contributing/installation.md b/py/engdoc/contributing/installation.md new file mode 100644 index 000000000..e69de29bb diff --git a/py/engdoc/contributing/troubleshooting.md b/py/engdoc/contributing/troubleshooting.md new file mode 100644 index 000000000..e69de29bb diff --git a/py/engdoc/extending/glossary.md b/py/engdoc/extending/glossary.md new file mode 100644 index 000000000..da2716702 --- /dev/null +++ b/py/engdoc/extending/glossary.md @@ -0,0 +1,14 @@ +# Glossary + +| **Term** | Definition | +|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Bi-encoder** | A model that compresses the meaning of a document or query into a single vector. Used in the first stage of retrieval. | +| **Context Stuffing** | Overloading the context window with too much information, which can degrade LLM performance. | +| **Context Window** | The maximum amount of text that an LLM can process at once. | +| **LLM Recall** | The ability of an LLM to find specific information within its context window. | +| **Recall** | A metric that measures how many relevant documents are retrieved in a search. | +| **Reranker (Cross-encoder)** | A model that takes a query and a document as input and outputs a similarity score. This score is used to reorder documents by relevance. | +| **Retrieval Augmented Generation (RAG)** | A technique that combines the power of Large Language Models (LLMs) with external knowledge sources to generate more accurate and comprehensive responses. | +| **Semantic Search** | Searching for information based on the meaning of words and phrases, rather than just matching keywords. | +| **Two-Stage Retrieval** | A system that first retrieves a large set of potentially relevant documents using a fast retriever (like a vector search) and then reranks them using a more accurate similarity score generated by a slower reranker before presenting them to an LLM. This approach combines the speed of the first-stage retrieval with the accuracy of the second-stage reranking, resulting in a more efficient and effective RAG pipeline. | +| **Vector Search** | A technique used to perform semantic search by converting text into numerical vectors and comparing their proximity in a vector space. | diff --git a/py/engdoc/extending/go/index.md b/py/engdoc/extending/go/index.md new file mode 100644 index 000000000..07dd0c5c7 --- /dev/null +++ b/py/engdoc/extending/go/index.md @@ -0,0 +1 @@ +# Overview diff --git a/py/engdoc/extending/index.md b/py/engdoc/extending/index.md new file mode 100644 index 000000000..5087eb147 --- /dev/null +++ b/py/engdoc/extending/index.md @@ -0,0 +1,254 @@ +# Framework Architecture + +!!! note + + If you're a user of Firebase Genkit and landed here, + this is engineering documentation that someone contributing + to Genkit would use, not necessarily only use it. + + For more information about how to get started with using + Firebase Genkit, please see: [User Guide](.) + +Firebase Genkit models a generative AI framework allowing application developers +to work with abstractions to allow the use of pluggable implementations of the +various elements of generative AI. It has SDKs for JavaScript, Go, and Python. + +## Design + +![Architecture Layers](../img/onion.svg) + +```d2 +genkit: { + veneer: Veneer API + ai: AI Components { + prompt: Prompt + flow: Flow + model: Model + } + core: Core Foundations { + actions: Actions + registry: Registry + reflection_server: Reflection Server + } + plugins: Plugins { + chroma + pinecone + vertex_ai + google_ai + google_cloud + openai + firebase + ollama + } +} + +lib: { + handlebars + dotprompt + pydantic + starlette + asgiref + uvicorn + opentelemetry +} + +genkit.ai -> genkit.core +genkit.ai -> lib.dotprompt +genkit.core -> lib.asgiref +genkit.core -> lib.opentelemetry +genkit.core -> lib.pydantic +genkit.core -> lib.starlette +genkit.plugins.chroma -> genkit.veneer +genkit.plugins.firebase -> genkit.veneer +genkit.plugins.google_ai -> genkit.veneer +genkit.plugins.google_cloud -> genkit.veneer +genkit.plugins.ollama -> genkit.veneer +genkit.plugins.pinecone -> genkit.veneer +genkit.plugins.vertex_ai -> genkit.veneer +genkit.veneer -> genkit.ai +genkit.veneer -> genkit.core +genkit.veneer -> lib.uvicorn +lib.dotprompt -> lib.handlebars +``` + +The framework has several layers of abstraction. Think about it as peeling an +onion, starting from the outermost layer: + +1. **User-facing Veneer** is the topmost layer consisting of the APIs exposed to + the application developers. + +2. **1st and 3rd Party Plugins** that extend the framework with additional + functionality. + +3. **AI Abstractions** are higher level generative AI components (e.g. tools, + agents, rerankers, embedders, vector stores, etc.) built atop the core + foundations. + +4. **Core Foundations** (actions and flows) are the primitive building blocks + upon which everything else has been built. Think of these as Lego bricks. + +5. **OpenTelemetry** is used to enable tracing. + +## User-friendly Veneer + +A **flow** is a remotely callable function that wraps user-defined +functionality. A **plugin** extends the framework by adding additional models, +parsers, retrievers, and other components generally used in AI applications. + +## AI Abstractions + +A **prompt** is an instruction provided to a model. Prompt engineering tweaks +these prompts to attempt to coax the model to do what you want. A **prompt +action** is used to render a prompt template producing a request that can be +passed to a **model**. Prompt actions are defined as either code or as +configuration files bearing the `.prompt` (read "dotprompt") extension. +**Dotprompt files** contain configuration in the form of YAML frontmatter +delineated by `---` sequences followed by variable-interpolated UTF-8 encoded +text templates (e.g. using a templating language such as Handlebars): + +```dotprompt +--- +model: vertexai/gemini-1.5-flash +config: + temperature: 0.9 +input: + schema: + properties: + location: {type: string} + style: {type: string} + name: {type: string} + required: [location] + default: + location: a restaurant +--- + +You are the most welcoming AI assistant and are currently working at {{location}}. + +Greet a guest{{#if name}} named {{name}}{{/if}}{{#if style}} in the style of {{style}}{{/if}}. +``` + +A **tool** is an action that can be used by a flow to complete tasks. An +**agent** can use tools (including other agents) to help automate complex tasks +and workflows. + +## Core Foundations + +An **action** is a locally or remotely (JSON based RPC) callable function. It is +strongly-typed, named, observable, uninterrupted operation that can be in +streaming or non-streaming mode. It wraps a function of type **Fn** that takes +an input of type **In**, and returns an output of type **Out**, optionally +streaming values of type **Stream** incrementally by invoking a +**StreamingCallback**. + +An action is a typed JSON-based RPC-over-HTTP function that supports metadata, +streaming, reflection and discovery. A flow is a user-defined action. An action +can depend on other actions. + +!!! note + + Bidirectional streaming is currently not supported. + +It can have **metadata** like description and specifies the structure for its +input and output using language-native typed schema validation (e.g, Zod for +TypeScript and Pydantic for Python) to generate backing JSON schemas usable by +underlying paraphernalia such as a **model** or **plugin**. Every action is +registered in a central in-memory **registry** exposed via a **RESTful +reflection API** that can be used to look up an action by name, test for +membership, and loaded for execution. + +!!! info "Obsolete definition" + + A **flow** used to be defined as an action that can be paused and resumed later, + even on a different machine. While such a feature is useful for long-running + tasks or situations where resources might become temporarily unavailable, this + role is now handled by **Sessions**. **FlowState** used to store the current + state of the flow, including its input, execution history, and cached steps. + **FlowExecution** used to represent a single run of the Flow's function. A + FlowState contains a history of FlowExecutions. + +Most components of Genkit such as tools, agents, prompts, models, retrievers, +embedders, evaluators, rerankers, and indexers are _framework-defined_ actions. +A **flow** is a _user-defined action_. + +**OpenTelemetry** integration is baked into each action. + +### Storage, Search and Indexing Actions + +| Term | Definition | +|------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Document** | A piece of **content** consisting of multiple **parts** and **metadata** that can be embedded. | +| **Embedder** | An action that embeds a document. | +| **Embedding** | A way to represent a discrete variable, like a word or a sentence, as a continuous vector. | +| **Evaluator** | An action that \_\_\_\_\_. | +| **Indexer** | An action that indexes content for future matching. | +| **Part** | A component of content that may have a content type (MIME type) and kind (text, media, data, tool request, tool response, etc.) associated with it. | +| **Reranker** (Cross-encoder) | A type of model that outputs a similarity score given a query and document pair. The score is used to reorder the documents by relevance to our query. | +| **Retriever** | An action that fetches documents from storage. | +| **Vector Store** | Stores vector embeddings and can be used to perform **similarity search** using algorithms such as cosine similarity, Euclidean distance, dot product, etc. | + +## Communication + +TODO + +## System Architecture Diagram + + +```d2 +vars: { + d2-config: { + layout-engine: elk + theme-id: 300 + sketch: true + } +} +runtime: "Runtime (Go, Python, Node.js, ...)" { + app: User Application { + shape: package + } + + library: "Library" { + plugins: "Plugins\n(pinecone,\ngoogleai,\nvertexai...)" + veneers: "Veneers\n(ai.generateFlow,\nai.defineFlow, ...)" + registry: { + shape: stored_data + style.multiple: true + } + otel: "OpenTelemetry" + actions: "Actions" + reflection_api: "Reflection API" + + plugins -> registry: Define models, etc. + plugins -> actions: Define + veneers -> registry + veneers -> actions + reflection_api -> registry: Lookup + reflection_api -> actions: Run + actions -> otel: Implement + } + + app -> library.plugins: Uses + app -> library.veneers: Uses +} +tooling: "Tooling" { + runtime_manager + dev_console: Developer Console UI { + shape: document + } + telemetry_server: Telemetry Server + trace_store: Trace Store { + shape: cylinder + } + eval_and_dataset_store: Evaluation & Dataset Store { + shape: cylinder + } + + dev_console -> runtime_manager: Uses + dev_console -> telemetry_server: Reports to + dev_console -> eval_and_dataset_store: Reads from and writes to + telemetry_server -> trace_store: Writes to +} + +runtime.library.otel -> tooling.telemetry_server: Reports to +tooling.runtime_manager -> runtime.library.reflection_api: Uses + +``` diff --git a/py/engdoc/extending/python/api.md b/py/engdoc/extending/python/api.md new file mode 100644 index 000000000..0b5b60b3b --- /dev/null +++ b/py/engdoc/extending/python/api.md @@ -0,0 +1,3 @@ +# API + +TODO: with mkdocstrings diff --git a/py/engdoc/extending/python/getting_started.md b/py/engdoc/extending/python/getting_started.md new file mode 100644 index 000000000..bad55622f --- /dev/null +++ b/py/engdoc/extending/python/getting_started.md @@ -0,0 +1 @@ +# Getting Started diff --git a/py/engdoc/extending/python/index.md b/py/engdoc/extending/python/index.md new file mode 100644 index 000000000..994641954 --- /dev/null +++ b/py/engdoc/extending/python/index.md @@ -0,0 +1,212 @@ +# Overview + +The Firebase Genkit Python AI SDK exposes components as remotely callable +functions called **actions** or **flows** via a reflection API using the +Asynchronous Server Gateway Interface +([ASGI](https://asgi.readthedocs.io/en/latest/specs/main.html)) over HTTP. + +An action is a typed JSON-based RPC-over-HTTP function that supports metadata, +streaming, reflection and discovery. A flow is a user-defined action. An action +can depend on other actions. + + +!!! note "Caveat" + + While a Python ASGI application implements bidirectional streaming, + the Genkit SDK defines only unidirectional streaming + callables for its actions. + +## Dealing with Concurrency + +Concurrency refers to the ability of our programs to deal with multiple, +potentially different, tasks all at once. This might mean interleaving tasks or +even running them in parallel. Parallelism requires multiple resources for +execution, whereas concurrent code isn't necessarily parallel and can apply even +to a single resource. There are several different ways of dealing with +concurrency among which are the following: + +| Model | Description | +|--------------------------|-------------------------------------------------------------------------------------------| +| Multiprocessing | Running multiple independent processes in a single program | +| Multithreading | Using multiple threads in a single process | +| Asynchronous programming | Handling I/O operations without blocking the main program flow | +| Coroutines | Lightweight resumable cooperatively-scheduled concurrent units of work | +| Goroutines (Go) and CSP | Lightweight, managed concurrent execution units that use messaging over concurrent queues | +| Actors | Independent units of computation that communicate via mailboxes | + + +The Genkit Python SDK makes use of coroutines, event loops, and asynchronous I/O +to manage concurrency. For synchronous situations, it makes use of [thread pool +executors](https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor). + +!!! info + + A **runnable** or **callable** is a unit of executable work—for example, + threads, processes, green threads, goroutines, and coroutines are runnables. + + **Asynchronous** runnables can wait for multiple steps to complete out of order + as opposed to **synchronous** runnables that require a specific order and cannot + proceed to execute subsequent steps without having completed waiting for any + previous blocking steps. The Python standard library offers an + [asyncio](https://docs.python.org/3/library/asyncio.html) module that works with + cooperatively-scheduled multitasking coroutines to manage I/O asynchronously. + + A **coroutine** is a resumable runnable that can pause its execution and then at + a later point in time resume from where it left off. Coroutines are: + + * **Non-blocking** because they do not block the thread of execution when paused + and yield control to the event loop to switch execution to another. + + * **Stateful** because they store their state when paused and resume from where + they left off and therefore do not require restarting. + + * **Cooperative** because they require the programmer to suspend cooperatively + at well-defined suspend points unlike threads that may be pre-emptively + scheduled. + + * **Cancelable** because Python supports terminating their execution by + bubbling up exceptions. + +## Handling Web Requests: WSGI vs ASGI + +WSGI and ASGI are both standards for Python Web applications, but they differ +significantly in how they handle requests and their overall capabilities. + +### WSGI (Web Server Gateway Interface) + +[WSGI](https://wsgi.readthedocs.io/en/latest/what.html), in essence, is embodied +by: + +!!! quote inline end + + "Call into this function with an HTTP request, and wait until an HTTP response + is returned." + +```python +def app(env, start_response): + start_response('200 OK', [('Content-Type','text/plain')]) + return [b"Hello World"] + + +``` + +WSGI is the yesteryear standard interface for Python Web applications. It is: + +* **Synchronous:** Processes one request at a time. Each request must be fully + completed before the server can move on to the next one. + +* **Blocking:** When a request is being processed, it "blocks" the server from + doing anything else until it's finished. + +* **Limited to HTTP:** WSGI only supports the HTTP protocol. + + +### ASGI (Asynchronous Server Gateway Interface) + +ASGI, in essence, is embodied by: + +!!! quote inline end + + "Here’s some information about the connection and the protocol, and a pair of + channels between which the server and application can communicate via event + streams." + +```python +async def app(scope, receive, send): + await logger.adebug('Debugging', scope=pformat(value)) + + await send({ + 'type': 'http.response.start', + 'status_code': 200, + 'headers': [], + }) + + # ... + + buffer = bytearray() + more = True + while more: + msg = await receive() + buffer.extend(await msg['body']) + more = msg.get('more_body', False) + + await send({ + 'type': 'http.response.body', + 'body': buffer, + }) + + # ... +``` + +[ASGI](https://asgi.readthedocs.io/en/latest/) (Asynchronous Server Gateway +Interface) is a spiritual successor to WSGI, intended to provide a standard +interface between async-capable Python Web servers, frameworks, and +applications. ASGI decomposes protocols into a series of _events_ that an +application must _receive_ and react to, and _events_ the application might +_send_ in response. + + +#### Applications and Deployment + +An ASGI application is: + +* **Asynchronous:** Can handle concurrent requests without waiting for each one + to finish and in no particular order. + +* **Non-blocking:** Allows the server to work on other tasks while waiting for a + request to complete (e.g., waiting for data from a database). + +* **Supports HTTP and WebSockets:** ASGI supports both HTTP for traditional Web + requests and WebSockets for real-time, two-way communication. + +* **Protocol-independent application** The ASGI protocol server handles the + nitty-gritties of managing connections, parsing input and rendering output in + the correct protocol format. It communicates with the ASGI application in the + form of event streams. + +An ASGI deployment artifact has 2 components: + +* **Protocol server**, which terminates sockets and translates them into + connections and per-connection event messages. Examples of protocol servers + are [Granian](https://github.com/emmett-framework/granian) and + [Uvicorn](https://www.uvicorn.org/). + +* **Application**, which lives inside a protocol server, is called once per + connection, and handles event messages as they happen, emitting its own event + messages back when necessary. + +![ASGI Deployment Artifact](../../img/asgi.svg) + +While one can write applications using the low-level primitives provided by +the ASGI specification, libraries such as +[FastAPI](https://fastapi.tiangolo.com/), +[Starlette](https://www.starlette.io/), and [Litestar](https://litestar.dev/) +provide more convenient interfaces for application development. + +### Key Differences Summarized + +| Feature | WSGI | ASGI | +|-----------------------|--------------------------------------------------|----------------------------------------------------------------------------| +| **Programming Model** | Synchronous, Blocking | Asynchronous, Non-blocking | +| **Concurrency** | Limited | Excellent | +| **Protocol Support** | HTTP Only | HTTP, WebSocket, HTTP/2 | +| **Use Cases** | Traditional Web applications (blogs, e-commerce) | Real-time applications (chat, online games), high-concurrency applications | + +#### When to Choose WSGI + +* For simpler Web applications that primarily serve content and don't require real-time features. + +* When using frameworks like Flask or Django (although Django now has ASGI support). + +#### When to Choose ASGI + +* For applications that need real-time, bidirectional communication (e.g., chat applications, online games). + +* When dealing with high traffic and concurrency. + +* When using modern asynchronous frameworks like Starlette or FastAPI. + +In essence, ASGI is the more modern and versatile standard, offering improved +performance and capabilities for demanding Web applications. However, WSGI +remains relevant for simpler projects where its limitations aren't a significant +concern. diff --git a/py/engdoc/extending/tooling/index.md b/py/engdoc/extending/tooling/index.md new file mode 100644 index 000000000..07dd0c5c7 --- /dev/null +++ b/py/engdoc/extending/tooling/index.md @@ -0,0 +1 @@ +# Overview diff --git a/py/engdoc/extending/typescript/index.md b/py/engdoc/extending/typescript/index.md new file mode 100644 index 000000000..07dd0c5c7 --- /dev/null +++ b/py/engdoc/extending/typescript/index.md @@ -0,0 +1 @@ +# Overview diff --git a/py/engdoc/img/asgi.svg b/py/engdoc/img/asgi.svg new file mode 100644 index 000000000..c7ed5de14 --- /dev/null +++ b/py/engdoc/img/asgi.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/py/engdoc/img/onion.svg b/py/engdoc/img/onion.svg new file mode 100644 index 000000000..509f9679e --- /dev/null +++ b/py/engdoc/img/onion.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/py/engdoc/index.md b/py/engdoc/index.md new file mode 100644 index 000000000..cf7c4bdb7 --- /dev/null +++ b/py/engdoc/index.md @@ -0,0 +1,571 @@ +# Genkit + +!!! note + + If you're a user of Firebase Genkit and landed here, + this is engineering documentation that someone contributing + to Genkit would use, not necessarily only use it. + + For more information about how to get started with using + Firebase Genkit, please see: [User Guide](.) + +## What is Genkit? + +Genkit is a framework designed to help you build AI-powered applications and +features. It provides open source libraries and plus developer +tools for testing and debugging. The following language runtimes are supported: + +| Language Runtime | Version | Support Tier | +|------------------|---------|--------------| +| Node.js | 22.0+ | 1 | +| Go | 1.22+ | 1 | +| Python | 3.12+ | 1 | + +It is designed to work with any generative AI model API or vector database. +While we offer integrations for Firebase and Google Cloud, you can use Genkit +independently of any Google services. + +The framework provides an abstraction of components by wrapping them with +building blocks called actions, each of which is maintained in a registry. An +action can expose a component over HTTP as a cloud function or server endpoint +and is inspectable and discoverable via a reflection API. Flows are actions +defined by the user and plugins can be created by third parties to extend the +set of available actions. + +## Key capabilities + +| Feature | Description | +|-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Unified API for AI generation** | Use one API to generate or stream content from various AI models. Works with multimodal input/output and custom model settings. | +| **Structured output** | Generate or stream structured objects (like JSON) with built-in validation. Simplify integration with your app and convert unstructured data into a usable format. | +| **Tool calling** | Let AI models call your functions and APIs as tools to complete tasks. The model decides when and which tools to use. | +| **Chat** | Genkit offers a chat-specific API that facilitates multi-turn conversations with AI models, which can be stateful and persistent. | +| **Agents** | Create intelligent agents that use tools (including other agents) to help automate complex tasks and workflows. | +| **Data retrieval** | Improve the accuracy and relevance of generated output by integrating your data. Simple APIs help you embed, index, and retrieve information from various sources. | +| **Prompt templating** | Create effective prompts that include rich text templating, model settings, multimodal support, and tool integration - all within a compact, runnable [prompt file](/docs/genkit/dotprompt). | + +See the following code samples for a concrete idea of how to use these +capabilities in code: + +### Feature Parity + +| Feature | Python | JavaScript | Go | +|-------------------|--------|------------|----| +| Agents | ❌ | ✅ | ✅ | +| Chat | ❌ | ✅ | ✅ | +| Data retrieval | ❌ | ✅ | ✅ | +| Generation | ❌ | ✅ | ✅ | +| Prompt templating | ❌ | ✅ | ✅ | +| Structured output | ❌ | ✅ | ✅ | +| Tool calling | ❌ | ✅ | ✅ | + +### Plugin Parity + +| Plugins | Python | JavaScript | Go | +|--------------|--------|------------|----| +| Chroma DB | ❌ | ✅ | ✅ | +| Dotprompt | ❌ | ✅ | ✅ | +| Firebase | ❌ | ✅ | ✅ | +| Google AI | ❌ | ✅ | ✅ | +| Google Cloud | ❌ | ✅ | ✅ | +| Ollama | ❌ | ✅ | ✅ | +| Pinecone | ❌ | ✅ | ✅ | +| Vertex AI | ❌ | ✅ | ✅ | + + +## Examples + +### Basic generation + +=== "Python" + + ```python hl_lines="12 13 14 15 17 20 21 22" linenums="1" + import asyncio + import structlog + + from genkit.veneer import genkit + from genkit.plugins.google_ai import googleAI + from genkit.plugins.google_ai.models import gemini15Flash + + logger = structlog.get_logger() + + + async def main() -> None: + ai = genkit({ # (1)! + plugins: [googleAI()], + model: gemini15Flash, + }) + + response = await ai.generate('Why is AI awesome?') + await logger.adebug(response.text) + + stream = await ai.generate_stream("Tell me a story") + async for chunk in stream: + await logger.adebug("Received chunk", text=chunk.text) + await logger.adebug("Finished generating text stream") + + + if __name__ == '__main__': + asyncio.run(content_generation()) + ``` + + 1. :man_raising_hand: Basic example of annotation. + +=== "JavaScript" + + ```javascript + import { genkit } from 'genkit'; + import { googleAI, gemini15Flash } from '@genkit-ai/googleai'; + + const ai = genkit({ + plugins: [googleAI()], + model: gemini15Flash, // Set default model + }); + + // Simple generation + const { text } = await ai.generate('Why is AI awesome?'); + console.log(text); + + // Streamed generation + const { stream } = await ai.generateStream('Tell me a story'); + for await (const chunk of stream) { + console.log(chunk.text); + } + ``` + +=== "Go" + + ```go + import "fmt" + + func main() { + fmt.Println("Hello") + } + ``` + + +### Structured output + +=== "Python" + + ```python + import asyncio + import logging + import structlog + + from genkit.veneer import genkit + from genkit.plugins.google_ai import googleAI + from genkit.plugins.google_ai.models import gemini15Flash + + logger = structlog.get_logger() + + from pydantic import BaseModel, Field, validator + from enum import Enum + + class Role(str, Enum): + KNIGHT = "knight" + MAGE = "mage" + ARCHER = "archer" + + class CharacterProfile(BaseModel): + name: str + role: Role + backstory: str + + async def main() -> None: + ai = genkit({ + plugins: [googleAI()], + model: gemini15Flash, + }) + + await logger.adebug("Generating structured output", prompt="Create a brief profile for a character in a fantasy video game.") + response = await ai.generate( + prompt="Create a brief profile for a character in a fantasy video game.", + output={ + "format": "json", + "schema": CharacterProfile, + }, + ) + await logger.ainfo("Generated output", output=response.output) + + + if __name__ == "__main__": + asyncio.run(main()) + ``` + +=== "JavaScript" + + ```javascript + import { genkit, z } from 'genkit'; + import { googleAI, gemini15Flash } from '@genkit-ai/googleai'; + + const ai = genkit({ + plugins: [googleAI()], + model: gemini15Flash, + }); + + const { output } = await ai.generate({ + prompt: 'Create a brief profile for a character in a fantasy video game.', + // Specify output structure using Zod schema + output: { + format: 'json', + schema: z.object({ + name: z.string(), + role: z.enum(['knight', 'mage', 'archer']), + backstory: z.string(), + }), + }, + + }); + + console.log(output); + ``` + +### Function calling + +=== "Python" + + ```python + import asyncio + import logging + import structlog + + from genkit.veneer import genkit + from genkit.plugins.google_ai import googleAI + from genkit.plugins.google_ai.models import gemini15Flash + from pydantic import BaseModel, Field + + logger = structlog.get_logger() + + + class GetWeatherInput(BaseModel): + location: str = Field(description="The location to get the current weather for") + + + class GetWeatherOutput(BaseModel): + weather: str + + + async def get_weather(input: GetWeatherInput) -> GetWeatherOutput: + await logger.adebug("Calling get_weather tool", location=input.location) + # Replace this with an actual API call to a weather service + weather_info = f"The current weather in {input.location} is 63°F and sunny." + return GetWeatherOutput(weather=weather_info) + + + async def main() -> None: + ai = genkit({ + plugins: [googleAI()], + model: gemini15Flash, + }) + + get_weather_tool = ai.define_tool( + name="getWeather", + description="Gets the current weather in a given location", + input_schema=GetWeatherInput, + output_schema=GetWeatherOutput, + func=get_weather, + ) + + await logger.adebug("Generating text with tool", prompt="What is the weather like in New York?") + response = await ai.generate( + prompt="What is the weather like in New York?", + tools=[get_weather_tool], + ) + await logger.ainfo("Generated text", text=response.text) + + + if __name__ == "__main__": + asyncio.run(main()) + ``` + +=== "JavaScript" + + ```javascript + import { genkit, z } from 'genkit'; + import { googleAI, gemini15Flash } from '@genkit-ai/googleai'; + + const ai = genkit({ + plugins: [googleAI()], + model: gemini15Flash, + }); + + // Define tool to get current weather for a given location + const getWeather = ai.defineTool( + { + name: "getWeather", + description: "Gets the current weather in a given location", + inputSchema: z.object({ + location: z.string().describe('The location to get the current weather for') + }), + outputSchema: z.string(), + }, + async (input) => { + // Here, we would typically make an API call or database query. For this + // example, we just return a fixed value. + return `The current weather in ${input.location} is 63°F and sunny.`; + } + ); + + const { text } = await ai.generate({ + tools: [getWeather], // Give the model a list of tools it can call + prompt: 'What is the weather like in New York? ', + }); + + console.log(text); + ``` + +### Chat + +=== "Python" + + ```python + import asyncio + import logging + import structlog + + from genkit.veneer import genkit + from genkit.plugins.google_ai import googleAI + from genkit.plugins.google_ai.models import gemini15Flash + from pydantic import BaseModel, Field + + logger = structlog.get_logger() + + + class ChatResponse(BaseModel): + text: str + + + async def chat(input: str) -> ChatResponse: + await logger.adebug("Calling chat tool", input=input) + # Replace this with an actual API call to a language model, + # providing the user query and the conversation history. + response_text = "Ahoy there! Your name is Pavel, you scurvy dog!" + return ChatResponse(text=response_text) + + + async def main() -> None: + ai = genkit({ + plugins: [googleAI()], + model: gemini15Flash, + }) + + chat_tool = ai.chat({system: 'Talk like a pirate'}) + + await logger.adebug("Calling chat tool", input="Hi, my name is Pavel") + response = await chat_tool.send("Hi, my name is Pavel") + + await logger.adebug("Calling chat tool", input="What is my name?") + response = await chat_tool.send("What is my name?") + + await logger.ainfo("Chat response", text=response.text) + + + if __name__ == "__main__": + asyncio.run(main()) + ``` + +=== "JavaScript" + + ```javascript + import { genkit, z } from 'genkit'; + import { googleAI, gemini15Flash } from '@genkit-ai/googleai'; + + const ai = genkit({ + plugins: [googleAI()], + model: gemini15Flash, + }); + + const chat = ai.chat({ system: 'Talk like a pirate' }); + + let response = await chat.send('Hi, my name is Pavel'); + + response = await chat.send('What is my name?'); + console.log(response.text); + // Ahoy there! Your name is Pavel, you scurvy dog + ``` +### Agents + +=== "Python" + +```python + +``` + +=== "JavaScript" + + ```javascript + import { genkit, z } from 'genkit'; + import { googleAI, gemini15Flash } from '@genkit-ai/googleai'; + + const ai = genkit({ + plugins: [googleAI()], + model: gemini15Flash, + }); + + // Define prompts that represent specialist agents + const reservationAgent = ai.definePrompt( + { + name: 'reservationAgent', + description: 'Reservation Agent can help manage guest reservations', + tools: [reservationTool, reservationCancelationTool, reservationListTool], + + }, + `{% verbatim %}{{role "system"}}{% endverbatim %} Help guests make and manage reservations` + ); + + const menuInfoAgent = ... + const complaintAgent = ... + + // Define a triage agent that routes to the proper specialist agent + const triageAgent = ai.definePrompt( + { + name: 'triageAgent', + description: 'Triage Agent', + tools: [reservationAgent, menuInfoAgent, complaintAgent], + }, + `{% verbatim %}{{role "system"}}{% endverbatim %} You are an AI customer service agent for Pavel's Cafe. + Greet the user and ask them how you can help. If appropriate, transfer to an + agent that can better handle the request. If you cannot help the customer + with the available tools, politely explain so.` + ); + + // Create a chat to enable multi-turn agent interactions + const chat = ai.chat(triageAgent); + + chat.send('I want a reservation at Pavel\'s Cafe for noon on Tuesday.' ); + ``` + +### Data retrieval + +=== "Python" + + ```python + import asyncio + import logging + import structlog + + from genkit.veneer import genkit + from genkit.plugins.google_ai import googleAI + from genkit.plugins.google_ai.models import gemini15Flash, textEmbedding004 + from genkit.plugins.dev_local_vectorstore import devLocalVectorstore, devLocalRetrieverRef + + logger = structlog.get_logger() + + + async def main() -> None: + ai = genkit( + plugins=[ + googleAI(), + devLocalVectorstore( + [ + { + "index_name": "BobFacts", + "embedder": textEmbedding004, + } + ] + ), + ], + model=gemini15Flash, + ) + + retriever = devLocalRetrieverRef("BobFacts") + + query = "How old is Bob?" + + await logger.adebug("Retrieving documents", query=query) + docs = await ai.retrieve(retriever=retriever, query=query) + + await logger.adebug("Generating answer", query=query) + response = await ai.generate( + prompt=f"Use the provided context from the BobFacts database to answer this query: {query}", + docs=docs, + ) + + await logger.ainfo("Generated answer", answer=response.text) + + if __name__ == "__main__": + asyncio.run(main()) + ``` + +=== "JavaScript" + + ```javascript + import { genkit } from 'genkit'; + import { googleAI, gemini15Flash, textEmbedding004 } from '@genkit-ai/googleai'; + import { devLocalRetrieverRef } from '@genkit-ai/dev-local-vectorstore'; + + const ai = genkit({ + plugins: [ + googleAI() + devLocalVectorstore([ + { + indexName: 'BobFacts', + embedder: textEmbedding004, + }, + ]), + ], + model: gemini15Flash, + }); + + // Reference to a local vector database storing Genkit documentation + const retriever = devLocalRetrieverRef('BobFacts'); + + // Consistent API to retrieve most relevant documents based on semantic similarity to query + const docs = await ai.retrieve( + retriever: retriever, + query: 'How old is bob?', + ); + + const result = await ai.generate({ + prompt: `Use the provided context from the Genkit documentation to answer this query: ${query}`, + docs // Pass retrieved documents to the model + }); + ``` + +### Prompt template + +=== "YAML" + + ```yaml + --- + model: vertexai/gemini-1.5-flash + config: + temperature: 0.9 + input: + schema: + properties: + location: {type: string} + style: {type: string} + name: {type: string} + required: [location] + default: + location: a restaurant + --- + + You are the most welcoming AI assistant and are currently working at {% + verbatim %}{{location}}{% endverbatim %}. + + Greet a guest{% verbatim %}{{#if name}}{% endverbatim %} named {% verbatim %}{{name}}{% endverbatim %}{% verbatim %}{{/if}}{% endverbatim %}{% verbatim %}{{#if style}}{% endverbatim %} in the style of {% verbatim %}{{style}}{% endverbatim %}{% verbatim %}{{/if}}{% endverbatim %}. + ``` + +## Development tools + +Genkit provides a command-line interface (CLI) and a local Developer UI to make +building AI applications easier. These tools help you: + +* **Experiment:** Test and refine your AI functions, prompts, and queries. +* **Debug:** Find and fix issues with detailed execution traces. +* **Evaluate:** Assess generated results across multiple test cases. + +## Connect with us + +* **Join the community:** Stay updated, ask questions, + and share your work on our [Discord server](https://discord.gg/qXt5zzQKpc). +* **Provide feedback:** Report issues or suggest new features + using our GitHub [issue tracker](https://github.com/firebase/genkit/issues). + +## Next steps + +Learn how to build your first AI application with Genkit in our [Get +started](/docs/get_started.md) guide. diff --git a/py/engdoc/user_guide/developer_tools.md b/py/engdoc/user_guide/developer_tools.md new file mode 100644 index 000000000..e69de29bb diff --git a/py/engdoc/user_guide/introduction.md b/py/engdoc/user_guide/introduction.md new file mode 100644 index 000000000..e69de29bb diff --git a/py/mkdocs.yml b/py/mkdocs.yml new file mode 100644 index 000000000..8945bbc09 --- /dev/null +++ b/py/mkdocs.yml @@ -0,0 +1,73 @@ +site_name: Firebase Genkit +repo_url: https://github.com/firebase/genkit +docs_dir: engdoc +theme: + name: material + palette: + - scheme: default + toggle: + icon: material/lightbulb + name: Switch to dark mode + - scheme: slate + toggle: + icon: material/lightbulb-outline + name: Switch to light mode + logo: assets/logo.png + favicon: assets/favicon.png + font: + text: Fira Sans + code: Fira Code + features: + - navigation.tabs + - navigation.instant + - navigation.instant.prefetch + - navigation.tracking + - navigation.footer + - content.code.copy + - content.code.annotate +markdown_extensions: + - admonition + - attr_list + - pymdownx.tabbed: + alternate_style: true + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + - pymdownx.details + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:mermaid2.fence_mermaid +plugins: + - autorefs + - d2 + - tags + - mermaid2 + - search + - mkdocstrings + - literate-nav: + nav_file: SUMMARY.md + - minify: + minify_html: true + minify_js: true + minify_css: true + htmlmin_opts: + remove_comments: true + cache_safe: true +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/firebase/genkit + - icon: fontawesome/brands/discord + link: https://discord.gg/qXt5zzQKpc + - icon: fontawesome/brands/youtube + link: https://www.youtube.com/user/Firebase + - icon: fontawesome/brands/x-twitter + link: https://x.com/Firebase diff --git a/py/packages/dotprompt/LICENSE b/py/packages/dotprompt/LICENSE new file mode 100644 index 000000000..220539673 --- /dev/null +++ b/py/packages/dotprompt/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/py/packages/dotprompt/README.md b/py/packages/dotprompt/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/py/packages/dotprompt/pyproject.toml b/py/packages/dotprompt/pyproject.toml new file mode 100644 index 000000000..bc344a7c8 --- /dev/null +++ b/py/packages/dotprompt/pyproject.toml @@ -0,0 +1,30 @@ +[project] +authors = [{ name = "Google" }] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries", +] +dependencies = [] +description = "Dotprompt is a language-neutral executable prompt template file format for Generative AI." +license = { text = "Apache-2.0" } +name = "dotprompt" +readme = "README.md" +requires-python = ">=3.12" +version = "0.1.0" + +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling"] + +[tool.hatch.build.targets.wheel] +packages = ["src/dotprompt"] diff --git a/py/packages/dotprompt/src/dotprompt/__init__.py b/py/packages/dotprompt/src/dotprompt/__init__.py new file mode 100644 index 000000000..6454f134c --- /dev/null +++ b/py/packages/dotprompt/src/dotprompt/__init__.py @@ -0,0 +1,2 @@ +def hello() -> str: + return 'Hello from dotprompt!' diff --git a/py/packages/dotprompt/src/dotprompt/py.typed b/py/packages/dotprompt/src/dotprompt/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/py/packages/genkit/LICENSE b/py/packages/genkit/LICENSE new file mode 100644 index 000000000..220539673 --- /dev/null +++ b/py/packages/genkit/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/py/packages/genkit/README.md b/py/packages/genkit/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/py/packages/genkit/pyproject.toml b/py/packages/genkit/pyproject.toml new file mode 100644 index 000000000..a41029702 --- /dev/null +++ b/py/packages/genkit/pyproject.toml @@ -0,0 +1,35 @@ +[project] +authors = [{ name = "Google" }] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries", +] +dependencies = [ + "opentelemetry-api>=1.29.0", + "opentelemetry-sdk>=1.29.0", + "pydantic>=2.10.5", + "requests>=2.32.3", +] +description = "Genkit AI Framework" +license = { text = "Apache-2.0" } +name = "genkit" +readme = "README.md" +requires-python = ">=3.12" +version = "0.1.0" + +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling"] + +[tool.hatch.build.targets.wheel] +packages = ["src/genkit"] diff --git a/py/packages/genkit/src/genkit/ai/__init__.py b/py/packages/genkit/src/genkit/ai/__init__.py new file mode 100644 index 000000000..cfa00dc6e --- /dev/null +++ b/py/packages/genkit/src/genkit/ai/__init__.py @@ -0,0 +1,24 @@ +# 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. + +""" +AI Foundations for Genkit +""" + + +def package_name() -> str: + return 'genkit.ai' + + +__all__ = ['package_name'] diff --git a/py/packages/genkit/src/genkit/ai/model.py b/py/packages/genkit/src/genkit/ai/model.py new file mode 100644 index 000000000..250056fff --- /dev/null +++ b/py/packages/genkit/src/genkit/ai/model.py @@ -0,0 +1,18 @@ +# Copyright 2025 Google Inc. +# +# 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. + +from typing import Callable +from genkit.core.types import GenerateRequest, GenerateResponse + +ModelFn = Callable[[GenerateRequest], GenerateResponse] diff --git a/py/packages/genkit/src/genkit/ai/prompt.py b/py/packages/genkit/src/genkit/ai/prompt.py new file mode 100644 index 000000000..71fa6c00c --- /dev/null +++ b/py/packages/genkit/src/genkit/ai/prompt.py @@ -0,0 +1,18 @@ +# Copyright 2025 Google Inc. +# +# 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. + +from genkit.core.types import GenerateRequest +from typing import Callable, Optional, Any + +PromptFn = Callable[[Optional[Any]], GenerateRequest] diff --git a/py/packages/genkit/src/genkit/core/__init__.py b/py/packages/genkit/src/genkit/core/__init__.py new file mode 100644 index 000000000..7d51106d6 --- /dev/null +++ b/py/packages/genkit/src/genkit/core/__init__.py @@ -0,0 +1,24 @@ +# 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. + +""" +Core Foundations for Genkit +""" + + +def package_name() -> str: + return 'genkit.core' + + +__all__ = ['package_name'] diff --git a/py/packages/genkit/src/genkit/core/action.py b/py/packages/genkit/src/genkit/core/action.py new file mode 100644 index 000000000..e3cb8d1eb --- /dev/null +++ b/py/packages/genkit/src/genkit/core/action.py @@ -0,0 +1,105 @@ +# Copyright 2025 Google Inc. +# +# 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 inspect +import json + +from typing import Dict, Optional, Callable, Any + +from pydantic import ConfigDict, BaseModel, TypeAdapter + +from genkit.core.tracing import tracer + + +class ActionResponse(BaseModel): + model_config = ConfigDict(extra='forbid') + + response: Any + traceId: str + + +class Action: + def __init__( + self, + type: str, + name: str, + fn: Callable, + description: str | None = None, + metadata: Optional[Dict[str, Any]] = None, + spanMetadata: Optional[Dict[str, str]] = None, + ): + self.type = type + self.name = name + + def fnToCall(*args, **kwargs): + with tracer.start_as_current_span(name) as span: + traceId = str(span.get_span_context().trace_id) + span.set_attribute('genkit:type', type) + span.set_attribute('genkit:name', name) + + if spanMetadata is not None: + for spanMetaKey in spanMetadata: + span.set_attribute( + spanMetaKey, spanMetadata[spanMetaKey] + ) + + if len(args) > 0: + if isinstance(args[0], BaseModel): + span.set_attribute( + 'genkit:input', args[0].model_dump_json() + ) + else: + span.set_attribute('genkit:input', json.dumps(args[0])) + + output = fn(*args, **kwargs) + + span.set_attribute('genkit:state', 'success') + + if isinstance(output, BaseModel): + span.set_attribute( + 'genkit:output', output.model_dump_json() + ) + else: + span.set_attribute('genkit:output', json.dumps(output)) + + return ActionResponse(response=output, traceId=traceId) + + self.fn = fnToCall + self.description = description + self.metadata = metadata + if self.metadata is None: + self.metadata = {} + + inputSpec = inspect.getfullargspec(fn) + actionArgs = list( + filter(lambda k: k != 'return', inputSpec.annotations) + ) + if len(actionArgs) > 1: + raise Exception('can only have one arg') + if len(actionArgs) > 0: + ta = TypeAdapter(inputSpec.annotations[actionArgs[0]]) + self.inputSchema = ta.json_schema() + self.inputType = ta + self.metadata['inputSchema'] = self.inputSchema + else: + self.inputSchema = TypeAdapter(Any).json_schema() + self.metadata['inputSchema'] = self.inputSchema + + if 'return' in inputSpec.annotations: + ta = TypeAdapter(inputSpec.annotations['return']) + self.outputSchema = ta.json_schema() + self.metadata['outputSchema'] = self.outputSchema + else: + self.outputSchema = TypeAdapter(Any).json_schema() + self.metadata['outputSchema'] = self.outputSchema diff --git a/py/packages/genkit/src/genkit/core/reflection.py b/py/packages/genkit/src/genkit/core/reflection.py new file mode 100644 index 000000000..f638c5e11 --- /dev/null +++ b/py/packages/genkit/src/genkit/core/reflection.py @@ -0,0 +1,106 @@ +# Copyright 2025 Google Inc. +# +# 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 json + +from http.server import BaseHTTPRequestHandler +from pydantic import BaseModel + +from genkit.core.registry import Registry + + +def MakeReflectionServer(registry: Registry): + class ReflectionServer(BaseHTTPRequestHandler): + def do_GET(self): + if self.path == '/api/__health': + self.send_response(200) + + elif self.path == '/api/actions': + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.end_headers() + + actions = {} + for type in registry.actions: + for name in registry.actions[type]: + action = registry.lookup_action(type, name) + key = f'/{type}/{name}' + actions[key] = { + 'key': key, + 'name': action.name, + 'inputSchema': action.inputSchema, + 'outputSchema': action.outputSchema, + 'metadata': action.metadata, + } + + self.wfile.write(bytes(json.dumps(actions), 'utf-8')) + + else: + self.send_response(404) + self.end_headers() + + def do_POST(self): + if self.path == '/api/notify': + self.send_response(200) + self.end_headers() + + elif self.path == '/api/runAction': + content_len = int(self.headers.get('Content-Length')) + post_body = self.rfile.read(content_len) + payload = json.loads(post_body.decode(encoding='utf-8')) + print(payload) + action = registry.lookup_by_absolute_name(payload['key']) + if '/flow/' in payload['key']: + input = action.inputType.validate_python( + payload['input']['start']['input'] + ) + else: + input = action.inputType.validate_python(payload['input']) + + output = action.fn(input) + + self.send_response(200) + self.send_header('x-genkit-version', '0.9.1') + self.send_header('Content-type', 'application/json') + self.end_headers() + + if isinstance(output.response, BaseModel): + self.wfile.write( + bytes( + '{"result": ' + + output.response.model_dump_json() + + ', "traceId": "' + + output.traceId + + '"}', + 'utf-8', + ) + ) + else: + self.wfile.write( + bytes( + json.dumps( + { + 'result': output.response, + 'telemetry': {'traceId': output.traceId}, + } + ), + 'utf-8', + ) + ) + + else: + self.send_response(404) + self.end_headers() + + return ReflectionServer diff --git a/py/packages/genkit/src/genkit/core/registry.py b/py/packages/genkit/src/genkit/core/registry.py new file mode 100644 index 000000000..c28348fdb --- /dev/null +++ b/py/packages/genkit/src/genkit/core/registry.py @@ -0,0 +1,34 @@ +# Copyright 2025 Google Inc. +# +# 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. + +from genkit.core.action import Action +from typing import Dict + + +class Registry: + actions: Dict[str, Dict[str, Action]] = {} + + def register_action(self, type: str, name: str, action: Action): + if type not in self.actions: + self.actions[type] = {} + self.actions[type][name] = action + + def lookup_action(self, type: str, name: str): + if type in self.actions and name in self.actions[type]: + return self.actions[type][name] + return None + + def lookup_by_absolute_name(self, name: str): + tkns = name.split('/', 2) + return self.lookup_action(tkns[1], tkns[2]) diff --git a/py/packages/genkit/src/genkit/core/tracing.py b/py/packages/genkit/src/genkit/core/tracing.py new file mode 100644 index 000000000..4b03e8447 --- /dev/null +++ b/py/packages/genkit/src/genkit/core/tracing.py @@ -0,0 +1,115 @@ +# Copyright 2025 Google Inc. +# +# 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 json +import os +import requests +import sys + +from typing import Any, Dict, Sequence +from opentelemetry import trace +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import ( + SpanExporter, + SpanExportResult, + SimpleSpanProcessor, +) +from opentelemetry import trace as trace_api +from opentelemetry.sdk.trace import ReadableSpan + + +class TelemetryServerSpanExporter(SpanExporter): + """Implementation of :class:`SpanExporter` that prints spans to the + console. + + This class can be used for diagnostic purposes. It prints the exported + spans to the console STDOUT. + """ + + def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: + for span in spans: + spanData = {'traceId': f'{span.context.trace_id}', 'spans': {}} + spanData['spans'][span.context.span_id] = { + 'spanId': f'{span.context.span_id}', + 'traceId': f'{span.context.trace_id}', + 'startTime': span.start_time / 1000000, + 'endTime': span.end_time / 1000000, + 'attributes': convert_attributes(span.attributes), + 'displayName': span.name, + # "links": span.links, + 'spanKind': trace_api.SpanKind(span.kind).name, + 'parentSpanId': f'{span.parent.span_id}' + if span.parent is not None + else None, + 'status': { + 'code': trace_api.StatusCode(span.status.status_code).value, + 'description': span.status.description, + } + if span.status is not None + else None, + 'instrumentationLibrary': { + 'name': 'genkit-tracer', + 'version': 'v1', + }, + # "timeEvents": { + # timeEvent: span.events.map((e)=> ({ + # time: transformTime(e.time), + # annotation: { + # attributes: e.attributes ?? {}, + # description: e.name, + # }, + # })), + # }, + } + if spanData['spans'][span.context.span_id]['parentSpanId'] is None: + del spanData['spans'][span.context.span_id]['parentSpanId'] + + if span.parent is None: + spanData['displayName'] = span.name + spanData['startTime'] = span.start_time + spanData['endTime'] = span.end_time + + # TODO: telemetry server URL must be dynamic, whatever tools notification says + requests.post( + 'http://localhost:4033/api/traces', + data=json.dumps(spanData), + headers={ + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + ) + + sys.stdout.flush() + return SpanExportResult.SUCCESS + + def force_flush(self, timeout_millis: int = 30000) -> bool: + return True + + +def convert_attributes(attributes: Dict[str, Any]) -> Dict[str, Any]: + attrs: Dict[str, Any] = {} + for key in attributes: + attrs[key] = attributes[key] + return attrs + + +if 'GENKIT_ENV' in os.environ and os.environ['GENKIT_ENV'] == 'dev': + provider = TracerProvider() + processor = SimpleSpanProcessor(TelemetryServerSpanExporter()) + provider.add_span_processor(processor) + # Sets the global default tracer provider + trace.set_tracer_provider(provider) + tracer = trace.get_tracer('genkit-tracer', 'v1', provider) +else: + tracer = trace.get_tracer('genkit-tracer', 'v1') diff --git a/py/packages/genkit/src/genkit/core/types.py b/py/packages/genkit/src/genkit/core/types.py new file mode 100644 index 000000000..293ddb7b1 --- /dev/null +++ b/py/packages/genkit/src/genkit/core/types.py @@ -0,0 +1,463 @@ +# Copyright 2022 Google Inc. +# +# 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. + + +# generated by datamodel-codegen: +# filename: genkit-schema.json +# timestamp: 2025-01-17T17:30:26+00:00 + +from __future__ import annotations + +from enum import Enum +from pydantic import ConfigDict, BaseModel, Field +from typing import Any, Dict, List, Optional, Union + + +class InstrumentationLibrary(BaseModel): + model_config = ConfigDict(extra='forbid') + + name: str + version: Optional[str] = None + schemaUrl: Optional[str] = None + + +class SpanContext(BaseModel): + model_config = ConfigDict(extra='forbid') + + traceId: str + spanId: str + isRemote: Optional[bool] = None + traceFlags: float + + +class SameProcessAsParentSpan(BaseModel): + model_config = ConfigDict(extra='forbid') + + value: bool + + +class State(Enum): + success = 'success' + error = 'error' + + +class SpanMetadata(BaseModel): + model_config = ConfigDict(extra='forbid') + + name: str + state: Optional[State] = None + input: Optional[Any] = None + output: Optional[Any] = None + isRoot: Optional[bool] = None + metadata: Optional[Dict[str, str]] = None + + +class SpanStatus(BaseModel): + model_config = ConfigDict(extra='forbid') + + code: float + message: Optional[str] = None + + +class Annotation(BaseModel): + model_config = ConfigDict(extra='forbid') + + attributes: Dict[str, Any] + description: str + + +class TimeEvent(BaseModel): + model_config = ConfigDict(extra='forbid') + + time: float + annotation: Annotation + + +class Code(Enum): + blocked = 'blocked' + other = 'other' + unknown = 'unknown' + + +class CandidateError(BaseModel): + model_config = ConfigDict(extra='forbid') + + index: float + code: Code + message: Optional[str] = None + + +class FinishReason(Enum): + stop = 'stop' + length = 'length' + blocked = 'blocked' + other = 'other' + unknown = 'unknown' + + +class DataPart(BaseModel): + model_config = ConfigDict(extra='forbid') + + text: Optional[Any] = None + media: Optional[Any] = None + toolRequest: Optional[Any] = None + toolResponse: Optional[Any] = None + data: Optional[Any] = None + metadata: Optional[Dict[str, Any]] = None + + +class Format(Enum): + json = 'json' + text = 'text' + media = 'media' + + +class Output(BaseModel): + model_config = ConfigDict(extra='forbid') + + format: Optional[Format] = None + schema_: Optional[Dict[str, Any]] = Field(None, alias='schema') + + +class Content(BaseModel): + model_config = ConfigDict(extra='forbid') + + text: str + media: Optional[Any] = None + + +class Media(BaseModel): + model_config = ConfigDict(extra='forbid') + + contentType: Optional[str] = None + url: str + + +class Content1(BaseModel): + model_config = ConfigDict(extra='forbid') + + text: Optional[Any] = None + media: Media + + +class ContextItem(BaseModel): + model_config = ConfigDict(extra='forbid') + + content: List[Union[Content, Content1]] + metadata: Optional[Dict[str, Any]] = None + + +class GenerationCommonConfig(BaseModel): + model_config = ConfigDict(extra='forbid') + + version: Optional[str] = None + temperature: Optional[float] = None + maxOutputTokens: Optional[float] = None + topK: Optional[float] = None + topP: Optional[float] = None + stopSequences: Optional[List[str]] = None + + +class GenerationUsage(BaseModel): + model_config = ConfigDict(extra='forbid') + + inputTokens: Optional[float] = None + outputTokens: Optional[float] = None + totalTokens: Optional[float] = None + inputCharacters: Optional[float] = None + outputCharacters: Optional[float] = None + inputImages: Optional[float] = None + outputImages: Optional[float] = None + inputVideos: Optional[float] = None + outputVideos: Optional[float] = None + inputAudioFiles: Optional[float] = None + outputAudioFiles: Optional[float] = None + custom: Optional[Dict[str, float]] = None + + +class Role(Enum): + system = 'system' + user = 'user' + model = 'model' + tool = 'tool' + + +class ToolDefinition(BaseModel): + model_config = ConfigDict(extra='forbid') + + name: str + description: str + inputSchema: Dict[str, Any] = Field( + ..., description='Valid JSON Schema representing the input of the tool.' + ) + outputSchema: Optional[Dict[str, Any]] = Field( + None, description='Valid JSON Schema describing the output of the tool.' + ) + metadata: Optional[Dict[str, Any]] = Field( + None, description='additional metadata for this tool definition' + ) + + +class ToolRequest1(BaseModel): + model_config = ConfigDict(extra='forbid') + + ref: Optional[str] = None + name: str + input: Optional[Any] = None + + +class ToolResponse1(BaseModel): + model_config = ConfigDict(extra='forbid') + + ref: Optional[str] = None + name: str + output: Optional[Any] = None + + +class Content2(BaseModel): + model_config = ConfigDict(extra='forbid') + + text: str + media: Optional[Any] = None + + +class Media2(BaseModel): + model_config = ConfigDict(extra='forbid') + + contentType: Optional[str] = None + url: str + + +class Content3(BaseModel): + model_config = ConfigDict(extra='forbid') + + text: Optional[Any] = None + media: Media2 + + +class Items(BaseModel): + model_config = ConfigDict(extra='forbid') + + content: List[Union[Content2, Content3]] + metadata: Optional[Dict[str, Any]] = None + + +class OutputModel(BaseModel): + model_config = ConfigDict(extra='forbid') + + format: Optional[Format] = None + schema_: Optional[Dict[str, Any]] = Field(None, alias='schema') + + +class Link(BaseModel): + model_config = ConfigDict(extra='forbid') + + context: Optional[SpanContext] = None + attributes: Optional[Dict[str, Any]] = None + droppedAttributesCount: Optional[float] = None + + +class TimeEvents(BaseModel): + model_config = ConfigDict(extra='forbid') + + timeEvent: Optional[List[TimeEvent]] = None + + +class SpanData(BaseModel): + model_config = ConfigDict(extra='forbid') + + spanId: str + traceId: str + parentSpanId: Optional[str] = None + startTime: float + endTime: float + attributes: Dict[str, Any] + displayName: str + links: Optional[List[Link]] = None + instrumentationLibrary: InstrumentationLibrary + spanKind: str + sameProcessAsParentSpan: Optional[SameProcessAsParentSpan] = None + status: Optional[SpanStatus] = None + timeEvents: Optional[TimeEvents] = None + truncated: Optional[bool] = None + + +class TraceData(BaseModel): + model_config = ConfigDict(extra='forbid') + + traceId: str + displayName: Optional[str] = None + startTime: Optional[float] = None + endTime: Optional[float] = None + spans: Dict[str, SpanData] + + +class MediaPart(BaseModel): + model_config = ConfigDict(extra='forbid') + + text: Optional[Any] = None + media: Media + toolRequest: Optional[Any] = None + toolResponse: Optional[Any] = None + data: Optional[Any] = None + metadata: Optional[Dict[str, Any]] = None + + +class Supports(BaseModel): + model_config = ConfigDict(extra='forbid') + + multiturn: Optional[bool] = None + media: Optional[bool] = None + tools: Optional[bool] = None + systemRole: Optional[bool] = None + output: Optional[List[Format]] = None + context: Optional[bool] = None + + +class ModelInfo(BaseModel): + model_config = ConfigDict(extra='forbid') + + versions: Optional[List[str]] = None + label: Optional[str] = None + supports: Optional[Supports] = None + + +class TextPart(BaseModel): + model_config = ConfigDict(extra='forbid') + + text: str + media: Optional[Any] = None + toolRequest: Optional[Any] = None + toolResponse: Optional[Any] = None + data: Optional[Any] = None + metadata: Optional[Dict[str, Any]] = None + + +class ToolRequestPart(BaseModel): + model_config = ConfigDict(extra='forbid') + + text: Optional[Any] = None + media: Optional[Any] = None + toolRequest: ToolRequest1 + toolResponse: Optional[Any] = None + data: Optional[Any] = None + metadata: Optional[Dict[str, Any]] = None + + +class ToolResponsePart(BaseModel): + model_config = ConfigDict(extra='forbid') + + text: Optional[Any] = None + media: Optional[Any] = None + toolRequest: Optional[Any] = None + toolResponse: ToolResponse1 + data: Optional[Any] = None + metadata: Optional[Dict[str, Any]] = None + + +class DocumentData(BaseModel): + model_config = ConfigDict(extra='forbid') + + content: List[ + Union[TextPart, MediaPart, ToolRequestPart, ToolResponsePart, DataPart] + ] + metadata: Optional[Dict[str, Any]] = None + + +class GenerateResponseChunk(BaseModel): + model_config = ConfigDict(extra='forbid') + + content: List[ + Union[TextPart, MediaPart, ToolRequestPart, ToolResponsePart, DataPart] + ] + custom: Optional[Any] = None + aggregated: Optional[bool] = None + index: float + + +class Message(BaseModel): + model_config = ConfigDict(extra='forbid') + + role: Role + content: List[ + Union[TextPart, MediaPart, ToolRequestPart, ToolResponsePart, DataPart] + ] + metadata: Optional[Dict[str, Any]] = None + + +class ModelResponseChunk(BaseModel): + model_config = ConfigDict(extra='forbid') + + content: List[ + Union[TextPart, MediaPart, ToolRequestPart, ToolResponsePart, DataPart] + ] + custom: Optional[Any] = None + aggregated: Optional[bool] = None + + +class Candidate(BaseModel): + model_config = ConfigDict(extra='forbid') + + index: float + message: Message + usage: Optional[GenerationUsage] = None + finishReason: FinishReason + finishMessage: Optional[str] = None + custom: Optional[Any] = None + + +class GenerateRequest(BaseModel): + model_config = ConfigDict(extra='forbid') + + messages: List[Message] + config: Optional[Any] = None + tools: Optional[List[ToolDefinition]] = None + output: Optional[Output] = None + context: Optional[List[ContextItem]] = None + candidates: Optional[float] = None + + +class GenerateResponse(BaseModel): + model_config = ConfigDict(extra='forbid') + + message: Optional[Message] = None + finishReason: Optional[FinishReason] = None + finishMessage: Optional[str] = None + latencyMs: Optional[float] = None + usage: Optional[GenerationUsage] = None + custom: Optional[Any] = None + request: Optional[GenerateRequest] = None + candidates: Optional[List[Candidate]] = None + + +class ModelRequest(BaseModel): + model_config = ConfigDict(extra='forbid') + + messages: List[Message] + config: Optional[Any] = None + tools: Optional[List[ToolDefinition]] = None + output: Optional[OutputModel] = None + context: Optional[List[Items]] = None + + +class ModelResponse(BaseModel): + model_config = ConfigDict(extra='forbid') + + message: Optional[Message] = None + finishReason: FinishReason + finishMessage: Optional[str] = None + latencyMs: Optional[float] = None + usage: Optional[GenerationUsage] = None + custom: Optional[Any] = None + request: Optional[GenerateRequest] = None diff --git a/py/packages/genkit/src/genkit/plugins/.gitignore b/py/packages/genkit/src/genkit/plugins/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/py/packages/genkit/src/genkit/py.typed b/py/packages/genkit/src/genkit/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/py/packages/genkit/src/genkit/veneer/__init__.py b/py/packages/genkit/src/genkit/veneer/__init__.py new file mode 100644 index 000000000..20224e957 --- /dev/null +++ b/py/packages/genkit/src/genkit/veneer/__init__.py @@ -0,0 +1,151 @@ +# Copyright 2025 Google Inc. +# +# 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 atexit +import datetime +import json +import os +import threading + +from http.server import HTTPServer +from typing import Union, List, Dict, Optional, Callable, Any + +from genkit.ai.model import ModelFn +from genkit.ai.prompt import PromptFn +from genkit.core.reflection import MakeReflectionServer +from genkit.core.registry import Registry +from genkit.core.action import Action +from genkit.core.types import GenerateRequest, GenerateResponse, Message + +Plugin = Callable[['Genkit'], None] + + +class Genkit: + registry: Registry = Registry() + + def __init__( + self, + plugins: Optional[List[Plugin]] = None, + model: Optional[str] = None, + ) -> None: + self.model = model + if 'GENKIT_ENV' in os.environ and os.environ['GENKIT_ENV'] == 'dev': + cwd = os.getcwd() + runtimesDir = os.path.join(cwd, '.genkit/runtimes') + current_datetime = datetime.datetime.now() + if not os.path.exists(runtimesDir): + os.makedirs(runtimesDir) + runtime_file_path = os.path.join( + runtimesDir, f'{current_datetime.isoformat()}.json' + ) + rf = open(runtime_file_path, 'w') + rf.write( + json.dumps( + { + 'id': f'{os.getpid()}', + 'pid': os.getpid(), + 'reflectionServerUrl': 'http://localhost:3100', + 'timestamp': f'{current_datetime.isoformat()}', + } + ) + ) + rf.close() + + def delete_runtime_file() -> None: + os.remove(runtime_file_path) + + atexit.register(delete_runtime_file) + + threading.Thread(target=self.start_server).start() + + if plugins is not None: + for plugin in plugins: + plugin(self) + + def start_server(self) -> None: + httpd = HTTPServer( + ('127.0.0.1', 3100), MakeReflectionServer(self.registry) + ) + httpd.serve_forever() + + def generate( + self, + model: Optional[str] = None, + prompt: Optional[Union[str]] = None, + messages: Optional[List[Message]] = None, + system: Optional[Union[str]] = None, + tools: Optional[List[str]] = None, + ) -> GenerateResponse: + model = model if model is not None else self.model + if model is None: + raise Exception('no model configured') + + modelAction = self.registry.lookup_action('model', model) + + return modelAction.fn(GenerateRequest(messages=messages)).response + + def flow( + self, name: Optional[str] = None + ) -> Callable[[Callable], Callable]: + def wrapper(func: Callable) -> Callable: + flowName = name if name is not None else func.__name__ + action = Action( + name=flowName, + type='flow', + fn=func, + spanMetadata={'genkit:metadata:flow:name': flowName}, + ) + self.registry.register_action( + type='flow', name=flowName, action=action + ) + + def decorator(*args: Any, **kwargs: Any) -> GenerateResponse: + return action.fn(*args, **kwargs).response + + return decorator + + return wrapper + + def define_model( + self, + name: str, + fn: ModelFn, + metadata: Optional[Dict[str, Any]] = None, + ) -> None: + action = Action(name=name, type='model', fn=fn, metadata=metadata) + self.registry.register_action('model', name, action) + + def define_prompt( + self, + name: str, + fn: PromptFn, + model: Optional[str] = None, + ) -> Callable[[Optional[Any]], GenerateResponse]: + def prompt(input: Optional[Any] = None) -> GenerateResponse: + req = fn(input) + return self.generate(messages=req.messages, model=model) + + action = Action('model', name, prompt) + self.registry.register_action('model', name, action) + + def wrapper(input: Optional[Any] = None) -> GenerateResponse: + return action.fn(input) + + return wrapper + + +__all__ = [ + 'Genkit', + 'Plugin', +] diff --git a/py/plugins/chroma/LICENSE b/py/plugins/chroma/LICENSE new file mode 100644 index 000000000..220539673 --- /dev/null +++ b/py/plugins/chroma/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/py/plugins/chroma/README.md b/py/plugins/chroma/README.md new file mode 100644 index 000000000..ae6ed13d9 --- /dev/null +++ b/py/plugins/chroma/README.md @@ -0,0 +1,3 @@ +# Chroma Firebase Genkit Plugin + +This Genkit plugin provides a set of tools and utilities for working with Chroma. diff --git a/py/plugins/chroma/pyproject.toml b/py/plugins/chroma/pyproject.toml new file mode 100644 index 000000000..e84e8ebc8 --- /dev/null +++ b/py/plugins/chroma/pyproject.toml @@ -0,0 +1,30 @@ +[project] +authors = [{ name = "Google" }] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries", +] +dependencies = ["genkit"] +description = "Genkit Chroma Plugin" +license = { text = "Apache-2.0" } +name = "genkit-chroma-plugin" +readme = "README.md" +requires-python = ">=3.12" +version = "0.1.0" + +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling"] + +[tool.hatch.build.targets.wheel] +packages = ["src/genkit", "src/genkit/plugins"] diff --git a/py/plugins/chroma/src/genkit/plugins/chroma/__init__.py b/py/plugins/chroma/src/genkit/plugins/chroma/__init__.py new file mode 100644 index 000000000..b1bde32b9 --- /dev/null +++ b/py/plugins/chroma/src/genkit/plugins/chroma/__init__.py @@ -0,0 +1,24 @@ +# 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. + +""" +Chroma Plugin for Genkit. +""" + + +def package_name() -> str: + return 'genkit.plugins.chroma' + + +__all__ = ['package_name'] diff --git a/py/plugins/chroma/src/genkit/py.typed b/py/plugins/chroma/src/genkit/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/py/plugins/firebase/LICENSE b/py/plugins/firebase/LICENSE new file mode 100644 index 000000000..220539673 --- /dev/null +++ b/py/plugins/firebase/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/py/plugins/firebase/README.md b/py/plugins/firebase/README.md new file mode 100644 index 000000000..285f89dfd --- /dev/null +++ b/py/plugins/firebase/README.md @@ -0,0 +1,4 @@ +# Genkit Firebase plugin + +This Genkit plugin provides a set of tools and utilities for working with +Firebase. diff --git a/py/plugins/firebase/pyproject.toml b/py/plugins/firebase/pyproject.toml new file mode 100644 index 000000000..5e0ccc1f5 --- /dev/null +++ b/py/plugins/firebase/pyproject.toml @@ -0,0 +1,30 @@ +[project] +authors = [{ name = "Google" }] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries", +] +dependencies = ["genkit"] +description = "Genkit Firebase Plugin" +license = { text = "Apache-2.0" } +name = "genkit-firebase-plugin" +readme = "README.md" +requires-python = ">=3.12" +version = "0.1.0" + +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling"] + +[tool.hatch.build.targets.wheel] +packages = ["src/genkit", "src/genkit/plugins"] diff --git a/py/plugins/firebase/src/genkit/plugins/firebase/__init__.py b/py/plugins/firebase/src/genkit/plugins/firebase/__init__.py new file mode 100644 index 000000000..74417b773 --- /dev/null +++ b/py/plugins/firebase/src/genkit/plugins/firebase/__init__.py @@ -0,0 +1,24 @@ +# 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. + +""" +Firebase Plugin for Genkit. +""" + + +def package_name() -> str: + return 'genkit.plugins.firebase' + + +__all__ = ['package_name'] diff --git a/py/plugins/firebase/src/genkit/py.typed b/py/plugins/firebase/src/genkit/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/py/plugins/google-ai/LICENSE b/py/plugins/google-ai/LICENSE new file mode 100644 index 000000000..220539673 --- /dev/null +++ b/py/plugins/google-ai/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/py/plugins/google-ai/README.md b/py/plugins/google-ai/README.md new file mode 100644 index 000000000..b2f1882d7 --- /dev/null +++ b/py/plugins/google-ai/README.md @@ -0,0 +1,4 @@ +# Genkit Google AI plugin + +This Genkit plugin provides a set of tools and utilities for working with +Google AI. diff --git a/py/plugins/google-ai/pyproject.toml b/py/plugins/google-ai/pyproject.toml new file mode 100644 index 000000000..c7314d06f --- /dev/null +++ b/py/plugins/google-ai/pyproject.toml @@ -0,0 +1,30 @@ +[project] +authors = [{ name = "Google" }] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries", +] +dependencies = ["genkit"] +description = "Genkit Google AI Plugin" +license = { text = "Apache-2.0" } +name = "genkit-google-ai-plugin" +readme = "README.md" +requires-python = ">=3.12" +version = "0.1.0" + +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling"] + +[tool.hatch.build.targets.wheel] +packages = ["src/genkit", "src/genkit/plugins"] diff --git a/py/plugins/google-ai/src/genkit/plugins/google_ai/__init__.py b/py/plugins/google-ai/src/genkit/plugins/google_ai/__init__.py new file mode 100644 index 000000000..801954c47 --- /dev/null +++ b/py/plugins/google-ai/src/genkit/plugins/google_ai/__init__.py @@ -0,0 +1,24 @@ +# 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. + +""" +Google AI Plugin for Genkit +""" + + +def package_name() -> str: + return 'genkit.plugins.google_ai' + + +__all__ = ['package_name'] diff --git a/py/plugins/google-ai/src/genkit/plugins/google_ai/models/__init__.py b/py/plugins/google-ai/src/genkit/plugins/google_ai/models/__init__.py new file mode 100644 index 000000000..1a3a0d5db --- /dev/null +++ b/py/plugins/google-ai/src/genkit/plugins/google_ai/models/__init__.py @@ -0,0 +1,24 @@ +# 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. + +""" +Google AI Models for Genkit. +""" + + +def package_name() -> str: + return 'genkit.plugins.google_ai.models' + + +__all__ = ['package_name'] diff --git a/py/plugins/google-ai/src/genkit/py.typed b/py/plugins/google-ai/src/genkit/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/py/plugins/google-cloud/LICENSE b/py/plugins/google-cloud/LICENSE new file mode 100644 index 000000000..220539673 --- /dev/null +++ b/py/plugins/google-cloud/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/py/plugins/google-cloud/README.md b/py/plugins/google-cloud/README.md new file mode 100644 index 000000000..3e6232034 --- /dev/null +++ b/py/plugins/google-cloud/README.md @@ -0,0 +1,4 @@ +# Google Cloud Plugin + +This Genkit plugin provides a set of tools and utilities for working with Google +Cloud. diff --git a/py/plugins/google-cloud/pyproject.toml b/py/plugins/google-cloud/pyproject.toml new file mode 100644 index 000000000..30beef996 --- /dev/null +++ b/py/plugins/google-cloud/pyproject.toml @@ -0,0 +1,30 @@ +[project] +authors = [{ name = "Google" }] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries", +] +dependencies = ["genkit"] +description = "Genkit Google Cloud Plugin" +license = { text = "Apache-2.0" } +name = "genkit-google-cloud-plugin" +readme = "README.md" +requires-python = ">=3.12" +version = "0.1.0" + +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling"] + +[tool.hatch.build.targets.wheel] +packages = ["src/genkit", "src/genkit/plugins"] diff --git a/py/plugins/google-cloud/src/genkit/plugins/google_cloud/__init__.py b/py/plugins/google-cloud/src/genkit/plugins/google_cloud/__init__.py new file mode 100644 index 000000000..9ebf26ecf --- /dev/null +++ b/py/plugins/google-cloud/src/genkit/plugins/google_cloud/__init__.py @@ -0,0 +1,24 @@ +# 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. + +""" +Google Cloud Plugin for Genkit. +""" + + +def package_name() -> str: + return 'genkit.plugins.google_cloud' + + +__all__ = ['package_name'] diff --git a/py/plugins/google-cloud/src/genkit/py.typed b/py/plugins/google-cloud/src/genkit/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/py/plugins/ollama/LICENSE b/py/plugins/ollama/LICENSE new file mode 100644 index 000000000..220539673 --- /dev/null +++ b/py/plugins/ollama/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/py/plugins/ollama/README.md b/py/plugins/ollama/README.md new file mode 100644 index 000000000..6cfe2070d --- /dev/null +++ b/py/plugins/ollama/README.md @@ -0,0 +1,3 @@ +# Ollama Plugin + +This Genkit plugin provides a set of tools and utilities for working with Ollama. diff --git a/py/plugins/ollama/pyproject.toml b/py/plugins/ollama/pyproject.toml new file mode 100644 index 000000000..8c20118cd --- /dev/null +++ b/py/plugins/ollama/pyproject.toml @@ -0,0 +1,30 @@ +[project] +authors = [{ name = "Google" }] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries", +] +dependencies = ["genkit"] +description = "Genkit Ollama Plugin" +license = { text = "Apache-2.0" } +name = "genkit-ollama-plugin" +readme = "README.md" +requires-python = ">=3.12" +version = "0.1.0" + +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling"] + +[tool.hatch.build.targets.wheel] +packages = ["src/genkit", "src/genkit/plugins"] diff --git a/py/plugins/ollama/src/genkit/plugins/ollama/__init__.py b/py/plugins/ollama/src/genkit/plugins/ollama/__init__.py new file mode 100644 index 000000000..ce95d9085 --- /dev/null +++ b/py/plugins/ollama/src/genkit/plugins/ollama/__init__.py @@ -0,0 +1,24 @@ +# 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. + +""" +Ollama Plugin for Genkit. +""" + + +def package_name() -> str: + return 'genkit.plugins.ollama' + + +__all__ = ['package_name'] diff --git a/py/plugins/ollama/src/genkit/plugins/ollama/models/__init__.py b/py/plugins/ollama/src/genkit/plugins/ollama/models/__init__.py new file mode 100644 index 000000000..4c44cab18 --- /dev/null +++ b/py/plugins/ollama/src/genkit/plugins/ollama/models/__init__.py @@ -0,0 +1,24 @@ +# 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. + +""" +Ollama Models for Genkit. +""" + + +def package_name() -> str: + return 'genkit.plugins.ollama.models' + + +__all__ = ['package_name'] diff --git a/py/plugins/ollama/src/genkit/py.typed b/py/plugins/ollama/src/genkit/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/py/plugins/pinecone/LICENSE b/py/plugins/pinecone/LICENSE new file mode 100644 index 000000000..220539673 --- /dev/null +++ b/py/plugins/pinecone/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/py/plugins/pinecone/README.md b/py/plugins/pinecone/README.md new file mode 100644 index 000000000..c7144e4c6 --- /dev/null +++ b/py/plugins/pinecone/README.md @@ -0,0 +1,4 @@ +# Pinecone Plugin + +This Genkit plugin provides a set of tools and utilities for working with +Pinecone. diff --git a/py/plugins/pinecone/pyproject.toml b/py/plugins/pinecone/pyproject.toml new file mode 100644 index 000000000..c33b3c6e9 --- /dev/null +++ b/py/plugins/pinecone/pyproject.toml @@ -0,0 +1,30 @@ +[project] +authors = [{ name = "Google" }] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries", +] +dependencies = ["genkit"] +description = "Genkit Pinecone Plugin" +license = { text = "Apache-2.0" } +name = "genkit-pinecone-plugin" +readme = "README.md" +requires-python = ">=3.12" +version = "0.1.0" + +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling"] + +[tool.hatch.build.targets.wheel] +packages = ["src/genkit", "src/genkit/plugins"] diff --git a/py/plugins/pinecone/src/genkit/plugins/pinecone/__init__.py b/py/plugins/pinecone/src/genkit/plugins/pinecone/__init__.py new file mode 100644 index 000000000..f93dbe3d4 --- /dev/null +++ b/py/plugins/pinecone/src/genkit/plugins/pinecone/__init__.py @@ -0,0 +1,24 @@ +# 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. + +""" +Pinecone Plugin for Genkit. +""" + + +def package_name() -> str: + return 'genkit.plugins.pinecone' + + +__all__ = ['package_name'] diff --git a/py/plugins/pinecone/src/genkit/py.typed b/py/plugins/pinecone/src/genkit/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/py/plugins/vertex-ai/LICENSE b/py/plugins/vertex-ai/LICENSE new file mode 100644 index 000000000..220539673 --- /dev/null +++ b/py/plugins/vertex-ai/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/py/plugins/vertex-ai/README.md b/py/plugins/vertex-ai/README.md new file mode 100644 index 000000000..36791583e --- /dev/null +++ b/py/plugins/vertex-ai/README.md @@ -0,0 +1,4 @@ +# Google Cloud Vertex AI Plugin + +This Genkit plugin provides a set of tools and utilities for working with Google +Cloud Vertex AI. diff --git a/py/plugins/vertex-ai/pyproject.toml b/py/plugins/vertex-ai/pyproject.toml new file mode 100644 index 000000000..ed1fd934f --- /dev/null +++ b/py/plugins/vertex-ai/pyproject.toml @@ -0,0 +1,30 @@ +[project] +authors = [{ name = "Google" }] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries", +] +dependencies = ["genkit", "google-cloud-aiplatform>=1.77.0"] +description = "Genkit Google Cloud Vertex AI Plugin" +license = { text = "Apache-2.0" } +name = "genkit-vertex-ai-plugin" +readme = "README.md" +requires-python = ">=3.12" +version = "0.1.0" + +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling"] + +[tool.hatch.build.targets.wheel] +packages = ["src/genkit", "src/genkit/plugins"] diff --git a/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/__init__.py b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/__init__.py new file mode 100644 index 000000000..a59cc1c58 --- /dev/null +++ b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/__init__.py @@ -0,0 +1,77 @@ +# 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. + +""" +Google Cloud Vertex AI Plugin for Genkit. +""" + +import vertexai + +from typing import Callable, Optional +from vertexai.generative_models import GenerativeModel, Content, Part + +from genkit.core.types import ( + GenerateRequest, + GenerateResponse, + Message, + TextPart, +) +from genkit.veneer import Genkit + + +def package_name() -> str: + return 'genkit.plugins.vertex_ai' + + +def vertexAI(project_id: Optional[str] = None) -> Callable[[Genkit], None]: + def plugin(ai: Genkit) -> None: + vertexai.init(location='us-central1', project=project_id) + + def gemini(request: GenerateRequest) -> GenerateResponse: + geminiMsgs: list[Content] = [] + for m in request.messages: + geminiParts: list[Part] = [] + for p in m.content: + if p.text is not None: + geminiParts.append(Part.from_text(p.text)) + else: + raise Exception('unsupported part type') + geminiMsgs.append(Content(role=m.role.value, parts=geminiParts)) + model = GenerativeModel('gemini-1.5-flash-002') + response = model.generate_content(contents=geminiMsgs) + return GenerateResponse( + message=Message( + role='model', content=[TextPart(text=response.text)] + ) + ) + + ai.define_model( + name='vertexai/gemini-1.5-flash', + fn=gemini, + metadata={ + 'model': { + 'label': 'banana', + 'supports': {'multiturn': True}, + } + }, + ) + + return plugin + + +def gemini(name: str) -> str: + return f'vertexai/{name}' + + +__all__ = ['package_name', 'vertexAI', 'gemini'] diff --git a/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/models/__init__.py b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/models/__init__.py new file mode 100644 index 000000000..fdd7ebeba --- /dev/null +++ b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/models/__init__.py @@ -0,0 +1,24 @@ +# 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. + +""" +Google Cloud Vertex AI Models for Genkit. +""" + + +def package_name() -> str: + return 'genkit.plugins.vertex_ai.models' + + +__all__ = ['package_name'] diff --git a/py/plugins/vertex-ai/src/genkit/py.typed b/py/plugins/vertex-ai/src/genkit/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/py/pyproject.toml b/py/pyproject.toml new file mode 100644 index 000000000..fad104ea0 --- /dev/null +++ b/py/pyproject.toml @@ -0,0 +1,80 @@ +[project] +dependencies = [ + "dotprompt", + "genkit", + "genkit-chroma-plugin", + "genkit-firebase-plugin", + "genkit-google-ai-plugin", + "genkit-google-cloud-plugin", + "genkit-ollama-plugin", + "genkit-pinecone-plugin", + "genkit-vertex-ai-plugin", +] +description = "Workspace for Genkit packages" +license = { text = "Apache-2.0" } +name = "genkit-workspace" +readme = "README.md" +requires-python = ">=3.12" +version = "0.1.0" + +[dependency-groups] +dev = [ + "bpython>=0.25", + "ipython>=8.31.0", + "jupyter>=1.1.1", + "pytest-asyncio>=0.25.2", + "pytest>=8.3.4", + "pytest-cov>=6.0.0", +] +lint = ["mypy>=1.14.1", "ruff>=0.9.2"] + +[tool.hatch.build.targets.wheel] +packages = [] + +[tool.setuptools] +py-modules = [] + +# Pytest for unit testing and coverage. +[tool.pytest] +asyncio_default_fixture_loop_scope = "function" +asyncio_mode = "strict" +python_files = [ + "packages/**/*_test.py", + "plugins/**/*_test.py", + "samples/**/*_test.py", +] +testpaths = ["packages", "plugins", "samples"] + +# uv based package management. +[tool.uv] +default-groups = ["dev", "lint"] + +[tool.uv.sources] +dotprompt = { workspace = true } +genkit = { workspace = true } +genkit-chroma-plugin = { workspace = true } +genkit-firebase-plugin = { workspace = true } +genkit-google-ai-plugin = { workspace = true } +genkit-google-cloud-plugin = { workspace = true } +genkit-ollama-plugin = { workspace = true } +genkit-pinecone-plugin = { workspace = true } +genkit-vertex-ai-plugin = { workspace = true } +hello = { workspace = true } + +[tool.uv.workspace] +members = ["packages/*", "plugins/*", "samples/*"] + +# Ruff checks and formatting. +[tool.ruff] +indent-width = 4 +line-length = 80 + +[tool.ruff.format] +line-ending = "lf" +quote-style = "single" + +# Static type checking. +[tool.mypy] +disallow_incomplete_defs = true +disallow_untyped_defs = true +warn_unused_configs = true diff --git a/py/samples/.gitignore b/py/samples/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/py/samples/hello/LICENSE b/py/samples/hello/LICENSE new file mode 100644 index 000000000..220539673 --- /dev/null +++ b/py/samples/hello/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/py/samples/hello/README.md b/py/samples/hello/README.md new file mode 100644 index 000000000..5df8f4ea9 --- /dev/null +++ b/py/samples/hello/README.md @@ -0,0 +1,18 @@ +# Hello world + +## Setup environment + +```bash +uv venv +source .venv/bin/activate +``` + +## Run the sample + +TODO + +```bash +genkit start -- python3 hello.py # Doesn't currently work with the venv configuration. +genkit start -- uv run hello.py # Starts but runtime detection fails. +genkit start -- uv run python3 hello.py # Starts but runtime detection fails. +``` diff --git a/py/samples/hello/hello.py b/py/samples/hello/hello.py new file mode 100644 index 000000000..1a1375a22 --- /dev/null +++ b/py/samples/hello/hello.py @@ -0,0 +1,66 @@ +# Copyright 2025 Google Inc. +# +# 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. + +from genkit.core.types import Message, TextPart, GenerateRequest +from genkit.plugins.vertex_ai import vertexAI, gemini +from genkit.veneer import Genkit +from pydantic import BaseModel, Field + +ai = Genkit(plugins=[vertexAI()], model=gemini('gemini-1.5-flash')) + + +class MyInput(BaseModel): + a: int = Field(description='a field') + b: int = Field(description='b field') + + +def hi_fn(input) -> GenerateRequest: + return GenerateRequest( + messages=[ + Message( + role='user', content=[TextPart(text='hi, my name is ' + input)] + ) + ] + ) + + +# hi = ai.define_prompt( +# name="hi", +# fn=hi_fn, +# model=gemini("gemini-1.5-flash")) +# +# @ai.flow() +# def hiPrompt(): +# return hi("Pavel") + + +@ai.flow() +def sayHi(input: str): + return ai.generate( + messages=[Message(role='user', content=[TextPart(text='hi ' + input)])] + ) + + +@ai.flow() +def sum_two_numbers2(input: MyInput): + return input.a + input.b + + +def main() -> None: + print(sayHi('John Doe')) + print(sum_two_numbers2(MyInput(a=1, b=3))) + + +if __name__ == '__main__': + main() diff --git a/py/samples/hello/pyproject.toml b/py/samples/hello/pyproject.toml new file mode 100644 index 000000000..f6430a1bd --- /dev/null +++ b/py/samples/hello/pyproject.toml @@ -0,0 +1,39 @@ +[project] +authors = [{ name = "Google" }] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries", +] +dependencies = [ + "genkit", + "genkit-firebase-plugin", + "genkit-google-ai-plugin", + "genkit-google-cloud-plugin", + "genkit-ollama-plugin", + "genkit-pinecone-plugin", + "genkit-vertex-ai-plugin", + "pydantic>=2.10.5", +] +description = "Hello world sample" +license = { text = "Apache-2.0" } +name = "hello" +readme = "README.md" +requires-python = ">=3.12" +version = "0.1.0" + +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling"] + +[tool.hatch.build.targets.wheel] +packages = ["src/hello"] diff --git a/py/taplo.toml b/py/taplo.toml new file mode 100644 index 000000000..928b99a3f --- /dev/null +++ b/py/taplo.toml @@ -0,0 +1,5 @@ +[formatting] +align_entries = true +array_trailing_comma = true +reorder_keys = true +trailing_newline = true diff --git a/py/tests/smoke/LICENSE b/py/tests/smoke/LICENSE new file mode 100644 index 000000000..220539673 --- /dev/null +++ b/py/tests/smoke/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/py/tests/smoke/README.md b/py/tests/smoke/README.md new file mode 100644 index 000000000..94bf8ffeb --- /dev/null +++ b/py/tests/smoke/README.md @@ -0,0 +1,4 @@ +# Packaging Smoke Test + +This is a smoke test for the packaging system +to ensure that our imports work as expected. diff --git a/py/tests/smoke/package_test.py b/py/tests/smoke/package_test.py new file mode 100644 index 000000000..9f72ec5cc --- /dev/null +++ b/py/tests/smoke/package_test.py @@ -0,0 +1,44 @@ +"""Smoke tests for package structure.""" + +# TODO: Replace this with proper imports once we have a proper implementation. +from genkit.ai import package_name as ai_package_name +from genkit.core import package_name as core_package_name +from genkit.plugins.chroma import package_name as chroma_package_name +from genkit.plugins.firebase import package_name as firebase_package_name +from genkit.plugins.google_ai import package_name as google_ai_package_name +from genkit.plugins.google_ai.models import ( + package_name as google_ai_models_package_name, +) +from genkit.plugins.google_cloud import ( + package_name as google_cloud_package_name, +) +from genkit.plugins.ollama import package_name as ollama_package_name +from genkit.plugins.pinecone import package_name as pinecone_package_name +from genkit.plugins.vertex_ai import package_name as vertex_ai_package_name +from genkit.plugins.vertex_ai.models import ( + package_name as vertex_ai_models_package_name, +) + + +def square(n: int | float) -> int | float: + return n * n + + +def test_package_names() -> None: + assert ai_package_name() == 'genkit.ai' + assert chroma_package_name() == 'genkit.plugins.chroma' + assert core_package_name() == 'genkit.core' + assert firebase_package_name() == 'genkit.plugins.firebase' + assert google_ai_models_package_name() == 'genkit.plugins.google_ai.models' + assert google_ai_package_name() == 'genkit.plugins.google_ai' + assert google_cloud_package_name() == 'genkit.plugins.google_cloud' + assert ollama_package_name() == 'genkit.plugins.ollama' + assert pinecone_package_name() == 'genkit.plugins.pinecone' + assert vertex_ai_models_package_name() == 'genkit.plugins.vertex_ai.models' + assert vertex_ai_package_name() == 'genkit.plugins.vertex_ai' + + +def test_square() -> None: + assert square(2) == 4 + assert square(3) == 9 + assert square(4) == 16 diff --git a/py/tests/smoke/pyproject.toml b/py/tests/smoke/pyproject.toml new file mode 100644 index 000000000..bd6ffc236 --- /dev/null +++ b/py/tests/smoke/pyproject.toml @@ -0,0 +1,37 @@ +[project] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries", +] +dependencies = [ + "genkit", + "genkit-firebase-plugin", + "genkit-google-ai-plugin", + "genkit-google-cloud-plugin", + "genkit-ollama-plugin", + "genkit-pinecone-plugin", + "genkit-vertex-ai-plugin", +] +description = "Packaging smoke test" +license = { text = "Apache-2.0" } +name = "smoke" +readme = "README.md" +requires-python = ">=3.12" +version = "0.1.0" + +[tool.pytest] +python_files = ["**/*_test.py"] +testpaths = ["."] + +[tool.hatch.build.targets.wheel] +packages = ["smoke"] diff --git a/py/uv.lock b/py/uv.lock new file mode 100644 index 000000000..51f6d9652 --- /dev/null +++ b/py/uv.lock @@ -0,0 +1,2397 @@ +version = 1 +requires-python = ">=3.12" +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version < '3.13'", +] + +[manifest] +members = [ + "dotprompt", + "genkit", + "genkit-chroma-plugin", + "genkit-firebase-plugin", + "genkit-google-ai-plugin", + "genkit-google-cloud-plugin", + "genkit-ollama-plugin", + "genkit-pinecone-plugin", + "genkit-vertex-ai-plugin", + "genkit-workspace", + "hello", +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "ansicon" +version = "1.89.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/e2/1c866404ddbd280efedff4a9f15abfe943cb83cde6e895022370f3a61f85/ansicon-1.89.0.tar.gz", hash = "sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1", size = 67312 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/f9/f1c10e223c7b56a38109a3f2eb4e7fe9a757ea3ed3a166754fb30f65e466/ansicon-1.89.0-py2.py3-none-any.whl", hash = "sha256:f1def52d17f65c2c9682cf8370c03f541f410c1752d6a14029f97318e4b9dfec", size = 63675 }, +] + +[[package]] +name = "anyio" +version = "4.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 }, +] + +[[package]] +name = "appnope" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, +] + +[[package]] +name = "argon2-cffi" +version = "23.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argon2-cffi-bindings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/31/fa/57ec2c6d16ecd2ba0cf15f3c7d1c3c2e7b5fcb83555ff56d7ab10888ec8f/argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08", size = 42798 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/6a/e8a041599e78b6b3752da48000b14c8d1e8a04ded09c88c714ba047f34f5/argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea", size = 15124 }, +] + +[[package]] +name = "argon2-cffi-bindings" +version = "21.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/e9/184b8ccce6683b0aa2fbb7ba5683ea4b9c5763f1356347f1312c32e3c66e/argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3", size = 1779911 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/13/838ce2620025e9666aa8f686431f67a29052241692a3dd1ae9d3692a89d3/argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367", size = 29658 }, + { url = "https://files.pythonhosted.org/packages/b3/02/f7f7bb6b6af6031edb11037639c697b912e1dea2db94d436e681aea2f495/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d", size = 80583 }, + { url = "https://files.pythonhosted.org/packages/ec/f7/378254e6dd7ae6f31fe40c8649eea7d4832a42243acaf0f1fff9083b2bed/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae", size = 86168 }, + { url = "https://files.pythonhosted.org/packages/74/f6/4a34a37a98311ed73bb80efe422fed95f2ac25a4cacc5ae1d7ae6a144505/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c", size = 82709 }, + { url = "https://files.pythonhosted.org/packages/74/2b/73d767bfdaab25484f7e7901379d5f8793cccbb86c6e0cbc4c1b96f63896/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86", size = 83613 }, + { url = "https://files.pythonhosted.org/packages/4f/fd/37f86deef67ff57c76f137a67181949c2d408077e2e3dd70c6c42912c9bf/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f", size = 84583 }, + { url = "https://files.pythonhosted.org/packages/6f/52/5a60085a3dae8fded8327a4f564223029f5f54b0cb0455a31131b5363a01/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e", size = 88475 }, + { url = "https://files.pythonhosted.org/packages/8b/95/143cd64feb24a15fa4b189a3e1e7efbaeeb00f39a51e99b26fc62fbacabd/argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082", size = 27698 }, + { url = "https://files.pythonhosted.org/packages/37/2c/e34e47c7dee97ba6f01a6203e0383e15b60fb85d78ac9a15cd066f6fe28b/argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f", size = 30817 }, + { url = "https://files.pythonhosted.org/packages/5a/e4/bf8034d25edaa495da3c8a3405627d2e35758e44ff6eaa7948092646fdcc/argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93", size = 53104 }, +] + +[[package]] +name = "arrow" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "types-python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/00/0f6e8fcdb23ea632c866620cc872729ff43ed91d284c866b515c6342b173/arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85", size = 131960 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80", size = 66419 }, +] + +[[package]] +name = "asttokens" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918 }, +] + +[[package]] +name = "async-lru" +version = "2.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/e2/2b4651eff771f6fd900d233e175ddc5e2be502c7eb62c0c42f975c6d36cd/async-lru-2.0.4.tar.gz", hash = "sha256:b8a59a5df60805ff63220b2a0c5b5393da5521b113cd5465a44eb037d81a5627", size = 10019 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/9f/3c3503693386c4b0f245eaf5ca6198e3b28879ca0a40bde6b0e319793453/async_lru-2.0.4-py3-none-any.whl", hash = "sha256:ff02944ce3c288c5be660c42dbcca0742b32c3b279d6dceda655190240b99224", size = 6111 }, +] + +[[package]] +name = "attrs" +version = "24.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/48/c8/6260f8ccc11f0917360fc0da435c5c9c7504e3db174d5a12a1494887b045/attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", size = 805984 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/aa/ab0f7891a01eeb2d2e338ae8fecbe57fcebea1a24dbb64d45801bfab481d/attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308", size = 63397 }, +] + +[[package]] +name = "babel" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "soupsieve" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 }, +] + +[[package]] +name = "bleach" +version = "6.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "webencodings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/9a/0e33f5054c54d349ea62c277191c020c2d6ef1d65ab2cb1993f91ec846d1/bleach-6.2.0.tar.gz", hash = "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f", size = 203083 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/55/96142937f66150805c25c4d0f31ee4132fd33497753400734f9dfdcbdc66/bleach-6.2.0-py3-none-any.whl", hash = "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e", size = 163406 }, +] + +[package.optional-dependencies] +css = [ + { name = "tinycss2" }, +] + +[[package]] +name = "blessed" +version = "1.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinxed", marker = "sys_platform == 'win32'" }, + { name = "six" }, + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/ae/92e9968ad23205389ec6bd82e2d4fca3817f1cdef34e10aa8d529ef8b1d7/blessed-1.20.0.tar.gz", hash = "sha256:2cdd67f8746e048f00df47a2880f4d6acbcdb399031b604e34ba8f71d5787680", size = 6655612 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/98/584f211c3a4bb38f2871fa937ee0cc83c130de50c955d6c7e2334dbf4acb/blessed-1.20.0-py2.py3-none-any.whl", hash = "sha256:0c542922586a265e699188e52d5f5ac5ec0dd517e5a1041d90d2bbf23f906058", size = 58372 }, +] + +[[package]] +name = "bpython" +version = "0.25" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "curtsies" }, + { name = "cwcwidth" }, + { name = "greenlet" }, + { name = "pygments" }, + { name = "pyxdg" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/dd/cc02bf66f342a4673867fdf6c1f9fce90ec1e91e651b21bc4af4890101da/bpython-0.25.tar.gz", hash = "sha256:c246fc909ef6dcc26e9d8cb4615b0e6b1613f3543d12269b19ffd0782166c65b", size = 207610 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/74/5470df025854d5e213793b62cbea032fd66919562662955789fcc5dc17d6/bpython-0.25-py3-none-any.whl", hash = "sha256:28fd86008ca5ef6100ead407c9743aa60c51293a18ba5b18fcacea7f5b7f2257", size = 176131 }, +] + +[[package]] +name = "cachetools" +version = "5.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/74/57df1ab0ce6bc5f6fa868e08de20df8ac58f9c44330c7671ad922d2bbeae/cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95", size = 28044 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/4e/de4ff18bcf55857ba18d3a4bd48c8a9fde6bb0980c9d20b263f05387fd88/cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb", size = 9530 }, +] + +[[package]] +name = "certifi" +version = "2024.12.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/bd/1d41ee578ce09523c81a15426705dd20969f5abf006d1afe8aeff0dd776a/certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db", size = 166010 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/32/8f6669fc4798494966bf446c8c4a162e0b5d893dff088afddf76414f70e1/certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", size = 164927 }, +] + +[[package]] +name = "cffi" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, + { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, + { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, + { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, + { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, + { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, + { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, + { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, + { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, + { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, + { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, + { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, + { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, + { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, + { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, + { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, + { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, + { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, + { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, + { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, + { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, + { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, + { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, + { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, + { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, + { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "comm" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/a8/fb783cb0abe2b5fded9f55e5703015cdf1c9c85b3669087c538dd15a6a86/comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e", size = 6210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/75/49e5bfe642f71f272236b5b2d2691cf915a7283cc0ceda56357b61daa538/comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3", size = 7180 }, +] + +[[package]] +name = "coverage" +version = "7.6.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/ba/ac14d281f80aab516275012e8875991bb06203957aa1e19950139238d658/coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23", size = 803868 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/77/19d09ea06f92fdf0487499283b1b7af06bc422ea94534c8fe3a4cd023641/coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853", size = 208281 }, + { url = "https://files.pythonhosted.org/packages/b6/67/5479b9f2f99fcfb49c0d5cf61912a5255ef80b6e80a3cddba39c38146cf4/coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078", size = 208514 }, + { url = "https://files.pythonhosted.org/packages/15/d1/febf59030ce1c83b7331c3546d7317e5120c5966471727aa7ac157729c4b/coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0", size = 241537 }, + { url = "https://files.pythonhosted.org/packages/4b/7e/5ac4c90192130e7cf8b63153fe620c8bfd9068f89a6d9b5f26f1550f7a26/coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50", size = 238572 }, + { url = "https://files.pythonhosted.org/packages/dc/03/0334a79b26ecf59958f2fe9dd1f5ab3e2f88db876f5071933de39af09647/coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022", size = 240639 }, + { url = "https://files.pythonhosted.org/packages/d7/45/8a707f23c202208d7b286d78ad6233f50dcf929319b664b6cc18a03c1aae/coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b", size = 240072 }, + { url = "https://files.pythonhosted.org/packages/66/02/603ce0ac2d02bc7b393279ef618940b4a0535b0868ee791140bda9ecfa40/coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0", size = 238386 }, + { url = "https://files.pythonhosted.org/packages/04/62/4e6887e9be060f5d18f1dd58c2838b2d9646faf353232dec4e2d4b1c8644/coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852", size = 240054 }, + { url = "https://files.pythonhosted.org/packages/5c/74/83ae4151c170d8bd071924f212add22a0e62a7fe2b149edf016aeecad17c/coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359", size = 210904 }, + { url = "https://files.pythonhosted.org/packages/c3/54/de0893186a221478f5880283119fc40483bc460b27c4c71d1b8bba3474b9/coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247", size = 211692 }, + { url = "https://files.pythonhosted.org/packages/25/6d/31883d78865529257bf847df5789e2ae80e99de8a460c3453dbfbe0db069/coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9", size = 208308 }, + { url = "https://files.pythonhosted.org/packages/70/22/3f2b129cc08de00c83b0ad6252e034320946abfc3e4235c009e57cfeee05/coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b", size = 208565 }, + { url = "https://files.pythonhosted.org/packages/97/0a/d89bc2d1cc61d3a8dfe9e9d75217b2be85f6c73ebf1b9e3c2f4e797f4531/coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690", size = 241083 }, + { url = "https://files.pythonhosted.org/packages/4c/81/6d64b88a00c7a7aaed3a657b8eaa0931f37a6395fcef61e53ff742b49c97/coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18", size = 238235 }, + { url = "https://files.pythonhosted.org/packages/9a/0b/7797d4193f5adb4b837207ed87fecf5fc38f7cc612b369a8e8e12d9fa114/coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c", size = 240220 }, + { url = "https://files.pythonhosted.org/packages/65/4d/6f83ca1bddcf8e51bf8ff71572f39a1c73c34cf50e752a952c34f24d0a60/coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd", size = 239847 }, + { url = "https://files.pythonhosted.org/packages/30/9d/2470df6aa146aff4c65fee0f87f58d2164a67533c771c9cc12ffcdb865d5/coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e", size = 237922 }, + { url = "https://files.pythonhosted.org/packages/08/dd/723fef5d901e6a89f2507094db66c091449c8ba03272861eaefa773ad95c/coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694", size = 239783 }, + { url = "https://files.pythonhosted.org/packages/3d/f7/64d3298b2baf261cb35466000628706ce20a82d42faf9b771af447cd2b76/coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6", size = 210965 }, + { url = "https://files.pythonhosted.org/packages/d5/58/ec43499a7fc681212fe7742fe90b2bc361cdb72e3181ace1604247a5b24d/coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e", size = 211719 }, + { url = "https://files.pythonhosted.org/packages/ab/c9/f2857a135bcff4330c1e90e7d03446b036b2363d4ad37eb5e3a47bbac8a6/coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe", size = 209050 }, + { url = "https://files.pythonhosted.org/packages/aa/b3/f840e5bd777d8433caa9e4a1eb20503495709f697341ac1a8ee6a3c906ad/coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273", size = 209321 }, + { url = "https://files.pythonhosted.org/packages/85/7d/125a5362180fcc1c03d91850fc020f3831d5cda09319522bcfa6b2b70be7/coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8", size = 252039 }, + { url = "https://files.pythonhosted.org/packages/a9/9c/4358bf3c74baf1f9bddd2baf3756b54c07f2cfd2535f0a47f1e7757e54b3/coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098", size = 247758 }, + { url = "https://files.pythonhosted.org/packages/cf/c7/de3eb6fc5263b26fab5cda3de7a0f80e317597a4bad4781859f72885f300/coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb", size = 250119 }, + { url = "https://files.pythonhosted.org/packages/3e/e6/43de91f8ba2ec9140c6a4af1102141712949903dc732cf739167cfa7a3bc/coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0", size = 249597 }, + { url = "https://files.pythonhosted.org/packages/08/40/61158b5499aa2adf9e37bc6d0117e8f6788625b283d51e7e0c53cf340530/coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf", size = 247473 }, + { url = "https://files.pythonhosted.org/packages/50/69/b3f2416725621e9f112e74e8470793d5b5995f146f596f133678a633b77e/coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2", size = 248737 }, + { url = "https://files.pythonhosted.org/packages/3c/6e/fe899fb937657db6df31cc3e61c6968cb56d36d7326361847440a430152e/coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312", size = 211611 }, + { url = "https://files.pythonhosted.org/packages/1c/55/52f5e66142a9d7bc93a15192eba7a78513d2abf6b3558d77b4ca32f5f424/coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d", size = 212781 }, +] + +[[package]] +name = "curtsies" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blessed" }, + { name = "cwcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/53/d2/ea91db929b5dcded637382235f9f1b7d06ef64b7f2af7fe1be1369e1f0d2/curtsies-0.4.2.tar.gz", hash = "sha256:6ebe33215bd7c92851a506049c720cca4cf5c192c1665c1d7a98a04c4702760e", size = 53559 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/ab/c4ae7ff01c75001829dfa54da9b25632a8206fa5c9036ea0292096b402d0/curtsies-0.4.2-py3-none-any.whl", hash = "sha256:f24d676a8c4711fb9edba1ab7e6134bc52305a222980b3b717bb303f5e94cec6", size = 35444 }, +] + +[[package]] +name = "cwcwidth" +version = "0.1.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/e3/275e359662052888bbb262b947d3f157aaf685aaeef4efc8393e4f36d8aa/cwcwidth-0.1.9.tar.gz", hash = "sha256:f19d11a0148d4a8cacd064c96e93bca8ce3415a186ae8204038f45e108db76b8", size = 57892 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/01/69a81a655ace57ce1423470ca29661a6821b66645ad4089e03d362a5c349/cwcwidth-0.1.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:42de102d5191fc68ef3ff6530f60c4895148ddc21aa0acaaf4612e5f7f0c38c4", size = 22281 }, + { url = "https://files.pythonhosted.org/packages/76/6a/00c1944f27116c1846ea3e84cc2f5d8711b213712d7e06183f1c49162fc3/cwcwidth-0.1.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:877e48c615b3fec88b7e640f9cf9d96704497657fb5aad2b7c0b0c59ecabff69", size = 101327 }, + { url = "https://files.pythonhosted.org/packages/fb/07/0389633bd61619000563a72d11387d98290cd1231ad3cfec964a845e0256/cwcwidth-0.1.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdbaf0a8dad20eb685df11a195a2449fe230b08a5b356d036c8d7e59d4128a88", size = 106554 }, + { url = "https://files.pythonhosted.org/packages/88/7c/5f84b644834e1a9ca41f7575bbace15f947fa46c1349b90f179843b47bc2/cwcwidth-0.1.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f6e0e023c4b127c47fd4c44cf537be209b9a28d8725f4f576f4d63744a23aa38", size = 102730 }, + { url = "https://files.pythonhosted.org/packages/6b/e1/69ff02feb0b10467b9fd0097650b1e4b6e0a2ad1ca32bcd1f936d18b27d8/cwcwidth-0.1.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b4f7d24236ce3c9d3b5e07fd75d232452f19bdddb6ae8bbfdcb97b6cb02835e8", size = 108551 }, + { url = "https://files.pythonhosted.org/packages/48/13/069554f659482f967cc380cac46f12a4cd2d55561a5f3dd0aebe900029ab/cwcwidth-0.1.9-cp312-cp312-win32.whl", hash = "sha256:ba9da6c911bf108334426890bc9f57b839a38e1afc4383a41bd70adbce470db3", size = 22223 }, + { url = "https://files.pythonhosted.org/packages/be/a2/462eebec8f0aa88751de678cbcdecd8b36ddf1ad05c25662541ef3e4455b/cwcwidth-0.1.9-cp312-cp312-win_amd64.whl", hash = "sha256:40466f16e85c338e8fc3eee87a8c9ca23416cc68b3049f68cb4cead5fb8b71b3", size = 24896 }, +] + +[[package]] +name = "debugpy" +version = "1.8.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/25/c74e337134edf55c4dfc9af579eccb45af2393c40960e2795a94351e8140/debugpy-1.8.12.tar.gz", hash = "sha256:646530b04f45c830ceae8e491ca1c9320a2d2f0efea3141487c82130aba70dce", size = 1641122 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/e6/0f876ecfe5831ebe4762b19214364753c8bc2b357d28c5d739a1e88325c7/debugpy-1.8.12-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:7e94b643b19e8feb5215fa508aee531387494bf668b2eca27fa769ea11d9f498", size = 2500846 }, + { url = "https://files.pythonhosted.org/packages/19/64/33f41653a701f3cd2cbff8b41ebaad59885b3428b5afd0d93d16012ecf17/debugpy-1.8.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:086b32e233e89a2740c1615c2f775c34ae951508b28b308681dbbb87bba97d06", size = 4222181 }, + { url = "https://files.pythonhosted.org/packages/32/a6/02646cfe50bfacc9b71321c47dc19a46e35f4e0aceea227b6d205e900e34/debugpy-1.8.12-cp312-cp312-win32.whl", hash = "sha256:2ae5df899732a6051b49ea2632a9ea67f929604fd2b036613a9f12bc3163b92d", size = 5227017 }, + { url = "https://files.pythonhosted.org/packages/da/a6/10056431b5c47103474312cf4a2ec1001f73e0b63b1216706d5fef2531eb/debugpy-1.8.12-cp312-cp312-win_amd64.whl", hash = "sha256:39dfbb6fa09f12fae32639e3286112fc35ae976114f1f3d37375f3130a820969", size = 5267555 }, + { url = "https://files.pythonhosted.org/packages/cf/4d/7c3896619a8791effd5d8c31f0834471fc8f8fb3047ec4f5fc69dd1393dd/debugpy-1.8.12-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:696d8ae4dff4cbd06bf6b10d671e088b66669f110c7c4e18a44c43cf75ce966f", size = 2485246 }, + { url = "https://files.pythonhosted.org/packages/99/46/bc6dcfd7eb8cc969a5716d858e32485eb40c72c6a8dc88d1e3a4d5e95813/debugpy-1.8.12-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:898fba72b81a654e74412a67c7e0a81e89723cfe2a3ea6fcd3feaa3395138ca9", size = 4218616 }, + { url = "https://files.pythonhosted.org/packages/03/dd/d7fcdf0381a9b8094da1f6a1c9f19fed493a4f8576a2682349b3a8b20ec7/debugpy-1.8.12-cp313-cp313-win32.whl", hash = "sha256:22a11c493c70413a01ed03f01c3c3a2fc4478fc6ee186e340487b2edcd6f4180", size = 5226540 }, + { url = "https://files.pythonhosted.org/packages/25/bd/ecb98f5b5fc7ea0bfbb3c355bc1dd57c198a28780beadd1e19915bf7b4d9/debugpy-1.8.12-cp313-cp313-win_amd64.whl", hash = "sha256:fdb3c6d342825ea10b90e43d7f20f01535a72b3a1997850c0c3cefa5c27a4a2c", size = 5267134 }, + { url = "https://files.pythonhosted.org/packages/38/c4/5120ad36405c3008f451f94b8f92ef1805b1e516f6ff870f331ccb3c4cc0/debugpy-1.8.12-py2.py3-none-any.whl", hash = "sha256:274b6a2040349b5c9864e475284bce5bb062e63dce368a394b8cc865ae3b00c6", size = 5229490 }, +] + +[[package]] +name = "decorator" +version = "5.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/0c/8d907af351aa16b42caae42f9d6aa37b900c67308052d10fdce809f8d952/decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", size = 35016 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 }, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 }, +] + +[[package]] +name = "deprecated" +version = "1.2.15" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2e/a3/53e7d78a6850ffdd394d7048a31a6f14e44900adedf190f9a165f6b69439/deprecated-1.2.15.tar.gz", hash = "sha256:683e561a90de76239796e6b6feac66b99030d2dd3fcf61ef996330f14bbb9b0d", size = 2977612 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/8f/c7f227eb42cfeaddce3eb0c96c60cbca37797fa7b34f8e1aeadf6c5c0983/Deprecated-1.2.15-py2.py3-none-any.whl", hash = "sha256:353bc4a8ac4bfc96800ddab349d89c25dec1079f65fd53acdcc1e0b975b21320", size = 9941 }, +] + +[[package]] +name = "docstring-parser" +version = "0.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/08/12/9c22a58c0b1e29271051222d8906257616da84135af9ed167c9e28f85cb3/docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e", size = 26565 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/7c/e9fcff7623954d86bdc17782036cbf715ecab1bec4847c008557affe1ca8/docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637", size = 36533 }, +] + +[[package]] +name = "dotprompt" +version = "0.1.0" +source = { editable = "packages/dotprompt" } + +[[package]] +name = "executing" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/e3/7d45f492c2c4a0e8e0fad57d081a7c8a0286cdd86372b070cca1ec0caa1e/executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab", size = 977485 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/fd/afcd0496feca3276f509df3dbd5dae726fcc756f1a08d9e25abe1733f962/executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf", size = 25805 }, +] + +[[package]] +name = "fastjsonschema" +version = "2.21.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/50/4b769ce1ac4071a1ef6d86b1a3fb56cdc3a37615e8c5519e1af96cdac366/fastjsonschema-2.21.1.tar.gz", hash = "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4", size = 373939 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/2b/0817a2b257fe88725c25589d89aec060581aabf668707a8d03b2e9e0cb2a/fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667", size = 23924 }, +] + +[[package]] +name = "fqdn" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121 }, +] + +[[package]] +name = "genkit" +version = "0.1.0" +source = { editable = "packages/genkit" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-sdk" }, + { name = "pydantic" }, + { name = "requests" }, +] + +[package.metadata] +requires-dist = [ + { name = "opentelemetry-api", specifier = ">=1.29.0" }, + { name = "opentelemetry-sdk", specifier = ">=1.29.0" }, + { name = "pydantic", specifier = ">=2.10.5" }, + { name = "requests", specifier = ">=2.32.3" }, +] + +[[package]] +name = "genkit-chroma-plugin" +version = "0.1.0" +source = { editable = "plugins/chroma" } +dependencies = [ + { name = "genkit" }, +] + +[package.metadata] +requires-dist = [{ name = "genkit", editable = "packages/genkit" }] + +[[package]] +name = "genkit-firebase-plugin" +version = "0.1.0" +source = { editable = "plugins/firebase" } +dependencies = [ + { name = "genkit" }, +] + +[package.metadata] +requires-dist = [{ name = "genkit", editable = "packages/genkit" }] + +[[package]] +name = "genkit-google-ai-plugin" +version = "0.1.0" +source = { editable = "plugins/google-ai" } +dependencies = [ + { name = "genkit" }, +] + +[package.metadata] +requires-dist = [{ name = "genkit", editable = "packages/genkit" }] + +[[package]] +name = "genkit-google-cloud-plugin" +version = "0.1.0" +source = { editable = "plugins/google-cloud" } +dependencies = [ + { name = "genkit" }, +] + +[package.metadata] +requires-dist = [{ name = "genkit", editable = "packages/genkit" }] + +[[package]] +name = "genkit-ollama-plugin" +version = "0.1.0" +source = { editable = "plugins/ollama" } +dependencies = [ + { name = "genkit" }, +] + +[package.metadata] +requires-dist = [{ name = "genkit", editable = "packages/genkit" }] + +[[package]] +name = "genkit-pinecone-plugin" +version = "0.1.0" +source = { editable = "plugins/pinecone" } +dependencies = [ + { name = "genkit" }, +] + +[package.metadata] +requires-dist = [{ name = "genkit", editable = "packages/genkit" }] + +[[package]] +name = "genkit-vertex-ai-plugin" +version = "0.1.0" +source = { editable = "plugins/vertex-ai" } +dependencies = [ + { name = "genkit" }, + { name = "google-cloud-aiplatform" }, +] + +[package.metadata] +requires-dist = [ + { name = "genkit", editable = "packages/genkit" }, + { name = "google-cloud-aiplatform", specifier = ">=1.77.0" }, +] + +[[package]] +name = "genkit-workspace" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "dotprompt" }, + { name = "genkit" }, + { name = "genkit-chroma-plugin" }, + { name = "genkit-firebase-plugin" }, + { name = "genkit-google-ai-plugin" }, + { name = "genkit-google-cloud-plugin" }, + { name = "genkit-ollama-plugin" }, + { name = "genkit-pinecone-plugin" }, + { name = "genkit-vertex-ai-plugin" }, +] + +[package.dev-dependencies] +dev = [ + { name = "bpython" }, + { name = "ipython" }, + { name = "jupyter" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, +] +lint = [ + { name = "mypy" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "dotprompt", editable = "packages/dotprompt" }, + { name = "genkit", editable = "packages/genkit" }, + { name = "genkit-chroma-plugin", editable = "plugins/chroma" }, + { name = "genkit-firebase-plugin", editable = "plugins/firebase" }, + { name = "genkit-google-ai-plugin", editable = "plugins/google-ai" }, + { name = "genkit-google-cloud-plugin", editable = "plugins/google-cloud" }, + { name = "genkit-ollama-plugin", editable = "plugins/ollama" }, + { name = "genkit-pinecone-plugin", editable = "plugins/pinecone" }, + { name = "genkit-vertex-ai-plugin", editable = "plugins/vertex-ai" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "bpython", specifier = ">=0.25" }, + { name = "ipython", specifier = ">=8.31.0" }, + { name = "jupyter", specifier = ">=1.1.1" }, + { name = "pytest", specifier = ">=8.3.4" }, + { name = "pytest-asyncio", specifier = ">=0.25.2" }, + { name = "pytest-cov", specifier = ">=6.0.0" }, +] +lint = [ + { name = "mypy", specifier = ">=1.14.1" }, + { name = "ruff", specifier = ">=0.9.2" }, +] + +[[package]] +name = "google-api-core" +version = "2.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "googleapis-common-protos" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/56/d70d66ed1b5ab5f6c27bf80ec889585ad8f865ff32acbafd3b2ef0bfb5d0/google_api_core-2.24.0.tar.gz", hash = "sha256:e255640547a597a4da010876d333208ddac417d60add22b6851a0c66a831fcaf", size = 162647 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/76/65b8b94e74bf1b6d1cc38d916089670c4da5029d25762441d8c5c19e51dd/google_api_core-2.24.0-py3-none-any.whl", hash = "sha256:10d82ac0fca69c82a25b3efdeefccf6f28e02ebb97925a8cce8edbfe379929d9", size = 158576 }, +] + +[package.optional-dependencies] +grpc = [ + { name = "grpcio" }, + { name = "grpcio-status" }, +] + +[[package]] +name = "google-auth" +version = "2.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "pyasn1-modules" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/af/b25763b9d35dfc2c6f9c3ec34d8d3f1ba760af3a7b7e8d5c5f0579522c45/google_auth-2.37.0.tar.gz", hash = "sha256:0054623abf1f9c83492c63d3f47e77f0a544caa3d40b2d98e099a611c2dd5d00", size = 268878 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/8d/4d5d5f9f500499f7bd4c93903b43e8d6976f3fc6f064637ded1a85d09b07/google_auth-2.37.0-py2.py3-none-any.whl", hash = "sha256:42664f18290a6be591be5329a96fe30184be1a1badb7292a7f686a9659de9ca0", size = 209829 }, +] + +[[package]] +name = "google-cloud-aiplatform" +version = "1.77.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docstring-parser" }, + { name = "google-api-core", extra = ["grpc"] }, + { name = "google-auth" }, + { name = "google-cloud-bigquery" }, + { name = "google-cloud-resource-manager" }, + { name = "google-cloud-storage" }, + { name = "packaging" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "pydantic" }, + { name = "shapely" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/45/7ffd099ff7554d9f4f3665611afb44d3ea59f8a3dd071e4284381d0ac3c1/google_cloud_aiplatform-1.77.0.tar.gz", hash = "sha256:1e5b77fe6c7f276d7aae65bcf08a273122a71f6c4af1f43cf45821f603a74080", size = 8287282 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/b6/f7a3c8bdb08a3636d216c49768eff3369b5475edd71f6dbe590a942252b9/google_cloud_aiplatform-1.77.0-py2.py3-none-any.whl", hash = "sha256:e9dd1bcb1b9a85eddd452916cd6ad1d9ce2d487772a9e45b1814aa0ac5633689", size = 6939280 }, +] + +[[package]] +name = "google-cloud-bigquery" +version = "3.29.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core", extra = ["grpc"] }, + { name = "google-auth" }, + { name = "google-cloud-core" }, + { name = "google-resumable-media" }, + { name = "packaging" }, + { name = "python-dateutil" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/36/87875a9775985849f18d4b3e320e4acdeb5232db3d49cfa6269e7c7867b8/google_cloud_bigquery-3.29.0.tar.gz", hash = "sha256:fafc2b455ffce3bcc6ce0e884184ef50b6a11350a83b91e327fadda4d5566e72", size = 467180 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/60/9e1430f0fe17f8e8e931eff468021516f74f2573f261221529767dd59591/google_cloud_bigquery-3.29.0-py2.py3-none-any.whl", hash = "sha256:5453a4eabe50118254eda9778f3d7dad413490de5f7046b5e66c98f5a1580308", size = 244605 }, +] + +[[package]] +name = "google-cloud-core" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b8/1f/9d1e0ba6919668608570418a9a51e47070ac15aeff64261fb092d8be94c0/google-cloud-core-2.4.1.tar.gz", hash = "sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073", size = 35587 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/0f/2e2061e3fbcb9d535d5da3f58cc8de4947df1786fe6a1355960feb05a681/google_cloud_core-2.4.1-py2.py3-none-any.whl", hash = "sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61", size = 29233 }, +] + +[[package]] +name = "google-cloud-resource-manager" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core", extra = ["grpc"] }, + { name = "google-auth" }, + { name = "grpc-google-iam-v1" }, + { name = "proto-plus" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/74/db14f34283b325b775b3287cd72ce8c43688bdea26801d02017a2ccded08/google_cloud_resource_manager-1.14.0.tar.gz", hash = "sha256:daa70a3a4704759d31f812ed221e3b6f7b660af30c7862e4a0060ea91291db30", size = 430148 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/c4/2275ca35419f9a2ae66846f389490b356856bf55a9ad9f95a88399a89294/google_cloud_resource_manager-1.14.0-py2.py3-none-any.whl", hash = "sha256:4860c3ea9ace760b317ea90d4e27f1b32e54ededdcc340a7cb70c8ef238d8f7c", size = 384138 }, +] + +[[package]] +name = "google-cloud-storage" +version = "2.19.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, + { name = "google-cloud-core" }, + { name = "google-crc32c" }, + { name = "google-resumable-media" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/76/4d965702e96bb67976e755bed9828fa50306dca003dbee08b67f41dd265e/google_cloud_storage-2.19.0.tar.gz", hash = "sha256:cd05e9e7191ba6cb68934d8eb76054d9be4562aa89dbc4236feee4d7d51342b2", size = 5535488 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/94/6db383d8ee1adf45dc6c73477152b82731fa4c4a46d9c1932cc8757e0fd4/google_cloud_storage-2.19.0-py2.py3-none-any.whl", hash = "sha256:aeb971b5c29cf8ab98445082cbfe7b161a1f48ed275822f59ed3f1524ea54fba", size = 131787 }, +] + +[[package]] +name = "google-crc32c" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/67/72/c3298da1a3773102359c5a78f20dae8925f5ea876e37354415f68594a6fb/google_crc32c-1.6.0.tar.gz", hash = "sha256:6eceb6ad197656a1ff49ebfbbfa870678c75be4344feb35ac1edf694309413dc", size = 14472 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/41/65a91657d6a8123c6c12f9aac72127b6ac76dda9e2ba1834026a842eb77c/google_crc32c-1.6.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ed767bf4ba90104c1216b68111613f0d5926fb3780660ea1198fc469af410e9d", size = 30268 }, + { url = "https://files.pythonhosted.org/packages/59/d0/ee743a267c7d5c4bb8bd865f7d4c039505f1c8a4b439df047fdc17be9769/google_crc32c-1.6.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:62f6d4a29fea082ac4a3c9be5e415218255cf11684ac6ef5488eea0c9132689b", size = 30113 }, + { url = "https://files.pythonhosted.org/packages/25/53/e5e449c368dd26ade5fb2bb209e046d4309ed0623be65b13f0ce026cb520/google_crc32c-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c87d98c7c4a69066fd31701c4e10d178a648c2cac3452e62c6b24dc51f9fcc00", size = 32995 }, + { url = "https://files.pythonhosted.org/packages/52/12/9bf6042d5b0ac8c25afed562fb78e51b0641474097e4139e858b45de40a5/google_crc32c-1.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd5e7d2445d1a958c266bfa5d04c39932dc54093fa391736dbfdb0f1929c1fb3", size = 32614 }, + { url = "https://files.pythonhosted.org/packages/76/29/fc20f5ec36eac1eea0d0b2de4118c774c5f59c513f2a8630d4db6991f3e0/google_crc32c-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:7aec8e88a3583515f9e0957fe4f5f6d8d4997e36d0f61624e70469771584c760", size = 33445 }, +] + +[[package]] +name = "google-resumable-media" +version = "2.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-crc32c" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/5a/0efdc02665dca14e0837b62c8a1a93132c264bd02054a15abb2218afe0ae/google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0", size = 2163099 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/35/b8d3baf8c46695858cb9d8835a53baa1eeb9906ddaf2f728a5f5b640fd1e/google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa", size = 81251 }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.66.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/a7/8e9cccdb1c49870de6faea2a2764fa23f627dd290633103540209f03524c/googleapis_common_protos-1.66.0.tar.gz", hash = "sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c", size = 114376 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/0f/c0713fb2b3d28af4b2fded3291df1c4d4f79a00d15c2374a9e010870016c/googleapis_common_protos-1.66.0-py2.py3-none-any.whl", hash = "sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed", size = 221682 }, +] + +[package.optional-dependencies] +grpc = [ + { name = "grpcio" }, +] + +[[package]] +name = "greenlet" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 }, + { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 }, + { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 }, + { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035 }, + { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105 }, + { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077 }, + { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 }, + { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 }, + { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 }, + { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 }, + { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 }, + { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 }, + { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 }, + { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 }, + { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 }, + { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 }, + { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 }, + { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 }, + { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 }, + { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 }, + { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 }, + { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 }, + { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 }, + { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 }, + { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 }, +] + +[[package]] +name = "grpc-google-iam-v1" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos", extra = ["grpc"] }, + { name = "grpcio" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/2f/68e43b0e551974fa7dd18798a5974710586a72dc484ecaa2fc023d961342/grpc_google_iam_v1-0.14.0.tar.gz", hash = "sha256:c66e07aa642e39bb37950f9e7f491f70dad150ac9801263b42b2814307c2df99", size = 18327 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/b4/ab54f7fda4af43ca5c094bc1d6341780fd669c44ae18952b5337029b1d98/grpc_google_iam_v1-0.14.0-py2.py3-none-any.whl", hash = "sha256:fb4a084b30099ba3ab07d61d620a0d4429570b13ff53bd37bac75235f98b7da4", size = 27276 }, +] + +[[package]] +name = "grpcio" +version = "1.69.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/87/06a145284cbe86c91ca517fe6b57be5efbb733c0d6374b407f0992054d18/grpcio-1.69.0.tar.gz", hash = "sha256:936fa44241b5379c5afc344e1260d467bee495747eaf478de825bab2791da6f5", size = 12738244 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/1d/8f28f147d7f3f5d6b6082f14e1e0f40d58e50bc2bd30d2377c730c57a286/grpcio-1.69.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fc18a4de8c33491ad6f70022af5c460b39611e39578a4d84de0fe92f12d5d47b", size = 5161414 }, + { url = "https://files.pythonhosted.org/packages/35/4b/9ab8ea65e515e1844feced1ef9e7a5d8359c48d986c93f3d2a2006fbdb63/grpcio-1.69.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:0f0270bd9ffbff6961fe1da487bdcd594407ad390cc7960e738725d4807b18c4", size = 11108909 }, + { url = "https://files.pythonhosted.org/packages/99/68/1856fde2b3c3162bdfb9845978608deef3606e6907fdc2c87443fce6ecd0/grpcio-1.69.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:dc48f99cc05e0698e689b51a05933253c69a8c8559a47f605cff83801b03af0e", size = 5658302 }, + { url = "https://files.pythonhosted.org/packages/3e/21/3fa78d38dc5080d0d677103fad3a8cd55091635cc2069a7c06c7a54e6c4d/grpcio-1.69.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e925954b18d41aeb5ae250262116d0970893b38232689c4240024e4333ac084", size = 6306201 }, + { url = "https://files.pythonhosted.org/packages/f3/cb/5c47b82fd1baf43dba973ae399095d51aaf0085ab0439838b4cbb1e87e3c/grpcio-1.69.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d222569273720366f68a99cb62e6194681eb763ee1d3b1005840678d4884f9", size = 5919649 }, + { url = "https://files.pythonhosted.org/packages/c6/67/59d1a56a0f9508a29ea03e1ce800bdfacc1f32b4f6b15274b2e057bf8758/grpcio-1.69.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b62b0f41e6e01a3e5082000b612064c87c93a49b05f7602fe1b7aa9fd5171a1d", size = 6648974 }, + { url = "https://files.pythonhosted.org/packages/f8/fe/ca70c14d98c6400095f19a0f4df8273d09c2106189751b564b26019f1dbe/grpcio-1.69.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:db6f9fd2578dbe37db4b2994c94a1d9c93552ed77dca80e1657bb8a05b898b55", size = 6215144 }, + { url = "https://files.pythonhosted.org/packages/b3/94/b2b0a9fd487fc8262e20e6dd0ec90d9fa462c82a43b4855285620f6e9d01/grpcio-1.69.0-cp312-cp312-win32.whl", hash = "sha256:b192b81076073ed46f4b4dd612b8897d9a1e39d4eabd822e5da7b38497ed77e1", size = 3644552 }, + { url = "https://files.pythonhosted.org/packages/93/99/81aec9f85412e3255a591ae2ccb799238e074be774e5f741abae08a23418/grpcio-1.69.0-cp312-cp312-win_amd64.whl", hash = "sha256:1227ff7836f7b3a4ab04e5754f1d001fa52a730685d3dc894ed8bc262cc96c01", size = 4399532 }, + { url = "https://files.pythonhosted.org/packages/54/47/3ff4501365f56b7cc16617695dbd4fd838c5e362bc7fa9fee09d592f7d78/grpcio-1.69.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:a78a06911d4081a24a1761d16215a08e9b6d4d29cdbb7e427e6c7e17b06bcc5d", size = 5162928 }, + { url = "https://files.pythonhosted.org/packages/c0/63/437174c5fa951052c9ecc5f373f62af6f3baf25f3f5ef35cbf561806b371/grpcio-1.69.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:dc5a351927d605b2721cbb46158e431dd49ce66ffbacb03e709dc07a491dde35", size = 11103027 }, + { url = "https://files.pythonhosted.org/packages/53/df/53566a6fdc26b6d1f0585896e1cc4825961039bca5a6a314ff29d79b5d5b/grpcio-1.69.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:3629d8a8185f5139869a6a17865d03113a260e311e78fbe313f1a71603617589", size = 5659277 }, + { url = "https://files.pythonhosted.org/packages/e6/4c/b8a0c4f71498b6f9be5ca6d290d576cf2af9d95fd9827c47364f023969ad/grpcio-1.69.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9a281878feeb9ae26db0622a19add03922a028d4db684658f16d546601a4870", size = 6305255 }, + { url = "https://files.pythonhosted.org/packages/ef/55/d9aa05eb3dfcf6aa946aaf986740ec07fc5189f20e2cbeb8c5d278ffd00f/grpcio-1.69.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc614e895177ab7e4b70f154d1a7c97e152577ea101d76026d132b7aaba003b", size = 5920240 }, + { url = "https://files.pythonhosted.org/packages/ea/eb/774b27c51e3e386dfe6c491a710f6f87ffdb20d88ec6c3581e047d9354a2/grpcio-1.69.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:1ee76cd7e2e49cf9264f6812d8c9ac1b85dda0eaea063af07292400f9191750e", size = 6652974 }, + { url = "https://files.pythonhosted.org/packages/59/98/96de14e6e7d89123813d58c246d9b0f1fbd24f9277f5295264e60861d9d6/grpcio-1.69.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:0470fa911c503af59ec8bc4c82b371ee4303ececbbdc055f55ce48e38b20fd67", size = 6215757 }, + { url = "https://files.pythonhosted.org/packages/7d/5b/ce922e0785910b10756fabc51fd294260384a44bea41651dadc4e47ddc82/grpcio-1.69.0-cp313-cp313-win32.whl", hash = "sha256:b650f34aceac8b2d08a4c8d7dc3e8a593f4d9e26d86751ebf74ebf5107d927de", size = 3642488 }, + { url = "https://files.pythonhosted.org/packages/5d/04/11329e6ca1ceeb276df2d9c316b5e170835a687a4d0f778dba8294657e36/grpcio-1.69.0-cp313-cp313-win_amd64.whl", hash = "sha256:028337786f11fecb5d7b7fa660475a06aabf7e5e52b5ac2df47414878c0ce7ea", size = 4399968 }, +] + +[[package]] +name = "grpcio-status" +version = "1.69.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpcio" }, + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/35/52dc0d8300f879dbf9cdc95764cee9f56d5a212998cfa1a8871b262df2a4/grpcio_status-1.69.0.tar.gz", hash = "sha256:595ef84e5178d6281caa732ccf68ff83259241608d26b0e9c40a5e66eee2a2d2", size = 13662 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/e2/346a766a4232f74f45f8bc70e636fc3a6677e6bc3893382187829085f12e/grpcio_status-1.69.0-py3-none-any.whl", hash = "sha256:d6b2a3c9562c03a817c628d7ba9a925e209c228762d6d7677ae5c9401a542853", size = 14428 }, +] + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + +[[package]] +name = "hello" +version = "0.1.0" +source = { editable = "samples/hello" } +dependencies = [ + { name = "genkit" }, + { name = "genkit-firebase-plugin" }, + { name = "genkit-google-ai-plugin" }, + { name = "genkit-google-cloud-plugin" }, + { name = "genkit-ollama-plugin" }, + { name = "genkit-pinecone-plugin" }, + { name = "genkit-vertex-ai-plugin" }, + { name = "pydantic" }, +] + +[package.metadata] +requires-dist = [ + { name = "genkit", editable = "packages/genkit" }, + { name = "genkit-firebase-plugin", editable = "plugins/firebase" }, + { name = "genkit-google-ai-plugin", editable = "plugins/google-ai" }, + { name = "genkit-google-cloud-plugin", editable = "plugins/google-cloud" }, + { name = "genkit-ollama-plugin", editable = "plugins/ollama" }, + { name = "genkit-pinecone-plugin", editable = "plugins/pinecone" }, + { name = "genkit-vertex-ai-plugin", editable = "plugins/vertex-ai" }, + { name = "pydantic", specifier = ">=2.10.5" }, +] + +[[package]] +name = "httpcore" +version = "1.0.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "ipykernel" +version = "6.29.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appnope", marker = "sys_platform == 'darwin'" }, + { name = "comm" }, + { name = "debugpy" }, + { name = "ipython" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "matplotlib-inline" }, + { name = "nest-asyncio" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/5c/67594cb0c7055dc50814b21731c22a601101ea3b1b50a9a1b090e11f5d0f/ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215", size = 163367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/5c/368ae6c01c7628438358e6d337c19b05425727fbb221d2a3c4303c372f42/ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5", size = 117173 }, +] + +[[package]] +name = "ipython" +version = "8.31.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/35/6f90fdddff7a08b7b715fccbd2427b5212c9525cd043d26fdc45bee0708d/ipython-8.31.0.tar.gz", hash = "sha256:b6a2274606bec6166405ff05e54932ed6e5cfecaca1fc05f2cacde7bb074d70b", size = 5501011 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/60/d0feb6b6d9fe4ab89fe8fe5b47cbf6cd936bfd9f1e7ffa9d0015425aeed6/ipython-8.31.0-py3-none-any.whl", hash = "sha256:46ec58f8d3d076a61d128fe517a51eb730e3aaf0c184ea8c17d16e366660c6a6", size = 821583 }, +] + +[[package]] +name = "ipywidgets" +version = "8.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "comm" }, + { name = "ipython" }, + { name = "jupyterlab-widgets" }, + { name = "traitlets" }, + { name = "widgetsnbextension" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/4c/dab2a281b07596a5fc220d49827fe6c794c66f1493d7a74f1df0640f2cc5/ipywidgets-8.1.5.tar.gz", hash = "sha256:870e43b1a35656a80c18c9503bbf2d16802db1cb487eec6fab27d683381dde17", size = 116723 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/2d/9c0b76f2f9cc0ebede1b9371b6f317243028ed60b90705863d493bae622e/ipywidgets-8.1.5-py3-none-any.whl", hash = "sha256:3290f526f87ae6e77655555baba4f36681c555b8bdbbff430b70e52c34c86245", size = 139767 }, +] + +[[package]] +name = "isoduration" +version = "20.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "arrow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321 }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278 }, +] + +[[package]] +name = "jinja2" +version = "3.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 }, +] + +[[package]] +name = "jinxed" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ansicon", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/20/d0/59b2b80e7a52d255f9e0ad040d2e826342d05580c4b1d7d7747cfb8db731/jinxed-1.3.0.tar.gz", hash = "sha256:1593124b18a41b7a3da3b078471442e51dbad3d77b4d4f2b0c26ab6f7d660dbf", size = 80981 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/e3/0e0014d6ab159d48189e92044ace13b1e1fe9aa3024ba9f4e8cf172aa7c2/jinxed-1.3.0-py2.py3-none-any.whl", hash = "sha256:b993189f39dc2d7504d802152671535b06d380b26d78070559551cbf92df4fc5", size = 33085 }, +] + +[[package]] +name = "json5" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/3d/bbe62f3d0c05a689c711cff57b2e3ac3d3e526380adb7c781989f075115c/json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559", size = 48202 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/42/797895b952b682c3dafe23b1834507ee7f02f4d6299b65aaa61425763278/json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa", size = 34049 }, +] + +[[package]] +name = "jsonpointer" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595 }, +] + +[[package]] +name = "jsonschema" +version = "4.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462 }, +] + +[package.optional-dependencies] +format-nongpl = [ + { name = "fqdn" }, + { name = "idna" }, + { name = "isoduration" }, + { name = "jsonpointer" }, + { name = "rfc3339-validator" }, + { name = "rfc3986-validator" }, + { name = "uri-template" }, + { name = "webcolors" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2024.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/db/58f950c996c793472e336ff3655b13fbcf1e3b359dcf52dcf3ed3b52c352/jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", size = 15561 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf", size = 18459 }, +] + +[[package]] +name = "jupyter" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ipykernel" }, + { name = "ipywidgets" }, + { name = "jupyter-console" }, + { name = "jupyterlab" }, + { name = "nbconvert" }, + { name = "notebook" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657 }, +] + +[[package]] +name = "jupyter-client" +version = "8.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-core" }, + { name = "python-dateutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105 }, +] + +[[package]] +name = "jupyter-console" +version = "6.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ipykernel" }, + { name = "ipython" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "pyzmq" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510 }, +] + +[[package]] +name = "jupyter-core" +version = "5.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/11/b56381fa6c3f4cc5d2cf54a7dbf98ad9aa0b339ef7a601d6053538b079a7/jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9", size = 87629 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/fb/108ecd1fe961941959ad0ee4e12ee7b8b1477247f30b1fdfd83ceaf017f0/jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409", size = 28965 }, +] + +[[package]] +name = "jupyter-events" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema", extra = ["format-nongpl"] }, + { name = "python-json-logger" }, + { name = "pyyaml" }, + { name = "referencing" }, + { name = "rfc3339-validator" }, + { name = "rfc3986-validator" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/65/5791c8a979b5646ca29ea50e42b6708908b789f7ff389d1a03c1b93a1c54/jupyter_events-0.11.0.tar.gz", hash = "sha256:c0bc56a37aac29c1fbc3bcfbddb8c8c49533f9cf11f1c4e6adadba936574ab90", size = 62039 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/8c/9b65cb2cd4ea32d885993d5542244641590530836802a2e8c7449a4c61c9/jupyter_events-0.11.0-py3-none-any.whl", hash = "sha256:36399b41ce1ca45fe8b8271067d6a140ffa54cec4028e95491c93b78a855cacf", size = 19423 }, +] + +[[package]] +name = "jupyter-lsp" +version = "2.2.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-server" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/b4/3200b0b09c12bc3b72d943d923323c398eff382d1dcc7c0dbc8b74630e40/jupyter-lsp-2.2.5.tar.gz", hash = "sha256:793147a05ad446f809fd53ef1cd19a9f5256fd0a2d6b7ce943a982cb4f545001", size = 48741 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/e0/7bd7cff65594fd9936e2f9385701e44574fc7d721331ff676ce440b14100/jupyter_lsp-2.2.5-py3-none-any.whl", hash = "sha256:45fbddbd505f3fbfb0b6cb2f1bc5e15e83ab7c79cd6e89416b248cb3c00c11da", size = 69146 }, +] + +[[package]] +name = "jupyter-server" +version = "2.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "argon2-cffi" }, + { name = "jinja2" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "jupyter-events" }, + { name = "jupyter-server-terminals" }, + { name = "nbconvert" }, + { name = "nbformat" }, + { name = "overrides" }, + { name = "packaging" }, + { name = "prometheus-client" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "pyzmq" }, + { name = "send2trash" }, + { name = "terminado" }, + { name = "tornado" }, + { name = "traitlets" }, + { name = "websocket-client" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/8c/df09d4ab646141f130f9977b32b206ba8615d1969b2eba6a2e84b7f89137/jupyter_server-2.15.0.tar.gz", hash = "sha256:9d446b8697b4f7337a1b7cdcac40778babdd93ba614b6d68ab1c0c918f1c4084", size = 725227 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/a2/89eeaf0bb954a123a909859fa507fa86f96eb61b62dc30667b60dbd5fdaf/jupyter_server-2.15.0-py3-none-any.whl", hash = "sha256:872d989becf83517012ee669f09604aa4a28097c0bd90b2f424310156c2cdae3", size = 385826 }, +] + +[[package]] +name = "jupyter-server-terminals" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "terminado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/d5/562469734f476159e99a55426d697cbf8e7eb5efe89fb0e0b4f83a3d3459/jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269", size = 31430 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa", size = 13656 }, +] + +[[package]] +name = "jupyterlab" +version = "4.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "async-lru" }, + { name = "httpx" }, + { name = "ipykernel" }, + { name = "jinja2" }, + { name = "jupyter-core" }, + { name = "jupyter-lsp" }, + { name = "jupyter-server" }, + { name = "jupyterlab-server" }, + { name = "notebook-shim" }, + { name = "packaging" }, + { name = "setuptools" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/45/1052f842e066902b1d78126df7e2269b1b9408991e1344e167b2e429f9e1/jupyterlab-4.3.4.tar.gz", hash = "sha256:f0bb9b09a04766e3423cccc2fc23169aa2ffedcdf8713e9e0fb33cac0b6859d0", size = 21797583 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/48/af57263e53cfc220e522de047aa0993f53bab734fe812af1e03e33ac6d7c/jupyterlab-4.3.4-py3-none-any.whl", hash = "sha256:b754c2601c5be6adf87cb5a1d8495d653ffb945f021939f77776acaa94dae952", size = 11665373 }, +] + +[[package]] +name = "jupyterlab-pygments" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884 }, +] + +[[package]] +name = "jupyterlab-server" +version = "2.27.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "jinja2" }, + { name = "json5" }, + { name = "jsonschema" }, + { name = "jupyter-server" }, + { name = "packaging" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/c9/a883ce65eb27905ce77ace410d83587c82ea64dc85a48d1f7ed52bcfa68d/jupyterlab_server-2.27.3.tar.gz", hash = "sha256:eb36caca59e74471988f0ae25c77945610b887f777255aa21f8065def9e51ed4", size = 76173 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/09/2032e7d15c544a0e3cd831c51d77a8ca57f7555b2e1b2922142eddb02a84/jupyterlab_server-2.27.3-py3-none-any.whl", hash = "sha256:e697488f66c3db49df675158a77b3b017520d772c6e1548c7d9bcc5df7944ee4", size = 59700 }, +] + +[[package]] +name = "jupyterlab-widgets" +version = "3.0.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/73/fa26bbb747a9ea4fca6b01453aa22990d52ab62dd61384f1ac0dc9d4e7ba/jupyterlab_widgets-3.0.13.tar.gz", hash = "sha256:a2966d385328c1942b683a8cd96b89b8dd82c8b8f81dda902bb2bc06d46f5bed", size = 203556 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/93/858e87edc634d628e5d752ba944c2833133a28fa87bb093e6832ced36a3e/jupyterlab_widgets-3.0.13-py3-none-any.whl", hash = "sha256:e3cda2c233ce144192f1e29914ad522b2f4c40e77214b0cc97377ca3d323db54", size = 214392 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, +] + +[[package]] +name = "mistune" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/6e/96fc7cb3288666c5de2c396eb0e338dc95f7a8e4920e43e38783a22d0084/mistune-3.1.0.tar.gz", hash = "sha256:dbcac2f78292b9dc066cd03b7a3a26b62d85f8159f2ea5fd28e55df79908d667", size = 94401 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b4/b3/743ffc3f59da380da504d84ccd1faf9a857a1445991ff19bf2ec754163c2/mistune-3.1.0-py3-none-any.whl", hash = "sha256:b05198cf6d671b3deba6c87ec6cf0d4eb7b72c524636eddb6dbf13823b52cee1", size = 53694 }, +] + +[[package]] +name = "mypy" +version = "1.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668 }, + { url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060 }, + { url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167 }, + { url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341 }, + { url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991 }, + { url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016 }, + { url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097 }, + { url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728 }, + { url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965 }, + { url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660 }, + { url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198 }, + { url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276 }, + { url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "nbclient" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "nbformat" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/66/7ffd18d58eae90d5721f9f39212327695b749e23ad44b3881744eaf4d9e8/nbclient-0.10.2.tar.gz", hash = "sha256:90b7fc6b810630db87a6d0c2250b1f0ab4cf4d3c27a299b0cde78a4ed3fd9193", size = 62424 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/6d/e7fa07f03a4a7b221d94b4d586edb754a9b0dc3c9e2c93353e9fa4e0d117/nbclient-0.10.2-py3-none-any.whl", hash = "sha256:4ffee11e788b4a27fabeb7955547e4318a5298f34342a4bfd01f2e1faaeadc3d", size = 25434 }, +] + +[[package]] +name = "nbconvert" +version = "7.16.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beautifulsoup4" }, + { name = "bleach", extra = ["css"] }, + { name = "defusedxml" }, + { name = "jinja2" }, + { name = "jupyter-core" }, + { name = "jupyterlab-pygments" }, + { name = "markupsafe" }, + { name = "mistune" }, + { name = "nbclient" }, + { name = "nbformat" }, + { name = "packaging" }, + { name = "pandocfilters" }, + { name = "pygments" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/2c/d026c0367f2be2463d4c2f5b538e28add2bc67bc13730abb7f364ae4eb8b/nbconvert-7.16.5.tar.gz", hash = "sha256:c83467bb5777fdfaac5ebbb8e864f300b277f68692ecc04d6dab72f2d8442344", size = 856367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/9e/2dcc9fe00cf55d95a8deae69384e9cea61816126e345754f6c75494d32ec/nbconvert-7.16.5-py3-none-any.whl", hash = "sha256:e12eac052d6fd03040af4166c563d76e7aeead2e9aadf5356db552a1784bd547", size = 258061 }, +] + +[[package]] +name = "nbformat" +version = "5.10.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastjsonschema" }, + { name = "jsonschema" }, + { name = "jupyter-core" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454 }, +] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, +] + +[[package]] +name = "notebook" +version = "7.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-server" }, + { name = "jupyterlab" }, + { name = "jupyterlab-server" }, + { name = "notebook-shim" }, + { name = "tornado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/04/ac488379d5afef43402b3fb4be2857db1a09804fecf98b9b714c741b225b/notebook-7.3.2.tar.gz", hash = "sha256:705e83a1785f45b383bf3ee13cb76680b92d24f56fb0c7d2136fe1d850cd3ca8", size = 12781804 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/9b/76e50ee18f183ea5fe1784a9eeaa50f2c71802e4740d6e959592b0993298/notebook-7.3.2-py3-none-any.whl", hash = "sha256:e5f85fc59b69d3618d73cf27544418193ff8e8058d5bf61d315ce4f473556288", size = 13163630 }, +] + +[[package]] +name = "notebook-shim" +version = "0.2.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-server" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307 }, +] + +[[package]] +name = "numpy" +version = "2.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/d0/c12ddfd3a02274be06ffc71f3efc6d0e457b0409c4481596881e748cb264/numpy-2.2.2.tar.gz", hash = "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f", size = 20233295 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/e6/847d15770ab7a01e807bdfcd4ead5bdae57c0092b7dc83878171b6af97bb/numpy-2.2.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467", size = 20912636 }, + { url = "https://files.pythonhosted.org/packages/d1/af/f83580891577b13bd7e261416120e036d0d8fb508c8a43a73e38928b794b/numpy-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a", size = 14098403 }, + { url = "https://files.pythonhosted.org/packages/2b/86/d019fb60a9d0f1d4cf04b014fe88a9135090adfadcc31c1fadbb071d7fa7/numpy-2.2.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825", size = 5128938 }, + { url = "https://files.pythonhosted.org/packages/7a/1b/50985edb6f1ec495a1c36452e860476f5b7ecdc3fc59ea89ccad3c4926c5/numpy-2.2.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37", size = 6661937 }, + { url = "https://files.pythonhosted.org/packages/f4/1b/17efd94cad1b9d605c3f8907fb06bcffc4ce4d1d14d46b95316cccccf2b9/numpy-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748", size = 14049518 }, + { url = "https://files.pythonhosted.org/packages/5b/73/65d2f0b698df1731e851e3295eb29a5ab8aa06f763f7e4188647a809578d/numpy-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0", size = 16099146 }, + { url = "https://files.pythonhosted.org/packages/d5/69/308f55c0e19d4b5057b5df286c5433822e3c8039ede06d4051d96f1c2c4e/numpy-2.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278", size = 15246336 }, + { url = "https://files.pythonhosted.org/packages/f0/d8/d8d333ad0d8518d077a21aeea7b7c826eff766a2b1ce1194dea95ca0bacf/numpy-2.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba", size = 17863507 }, + { url = "https://files.pythonhosted.org/packages/82/6e/0b84ad3103ffc16d6673e63b5acbe7901b2af96c2837174c6318c98e27ab/numpy-2.2.2-cp312-cp312-win32.whl", hash = "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283", size = 6276491 }, + { url = "https://files.pythonhosted.org/packages/fc/84/7f801a42a67b9772a883223a0a1e12069a14626c81a732bd70aac57aebc1/numpy-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb", size = 12616372 }, + { url = "https://files.pythonhosted.org/packages/e1/fe/df5624001f4f5c3e0b78e9017bfab7fdc18a8d3b3d3161da3d64924dd659/numpy-2.2.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc", size = 20899188 }, + { url = "https://files.pythonhosted.org/packages/a9/80/d349c3b5ed66bd3cb0214be60c27e32b90a506946857b866838adbe84040/numpy-2.2.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369", size = 14113972 }, + { url = "https://files.pythonhosted.org/packages/9d/50/949ec9cbb28c4b751edfa64503f0913cbfa8d795b4a251e7980f13a8a655/numpy-2.2.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd", size = 5114294 }, + { url = "https://files.pythonhosted.org/packages/8d/f3/399c15629d5a0c68ef2aa7621d430b2be22034f01dd7f3c65a9c9666c445/numpy-2.2.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be", size = 6648426 }, + { url = "https://files.pythonhosted.org/packages/2c/03/c72474c13772e30e1bc2e558cdffd9123c7872b731263d5648b5c49dd459/numpy-2.2.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84", size = 14045990 }, + { url = "https://files.pythonhosted.org/packages/83/9c/96a9ab62274ffafb023f8ee08c88d3d31ee74ca58869f859db6845494fa6/numpy-2.2.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff", size = 16096614 }, + { url = "https://files.pythonhosted.org/packages/d5/34/cd0a735534c29bec7093544b3a509febc9b0df77718a9b41ffb0809c9f46/numpy-2.2.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0", size = 15242123 }, + { url = "https://files.pythonhosted.org/packages/5e/6d/541717a554a8f56fa75e91886d9b79ade2e595918690eb5d0d3dbd3accb9/numpy-2.2.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de", size = 17859160 }, + { url = "https://files.pythonhosted.org/packages/b9/a5/fbf1f2b54adab31510728edd06a05c1b30839f37cf8c9747cb85831aaf1b/numpy-2.2.2-cp313-cp313-win32.whl", hash = "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9", size = 6273337 }, + { url = "https://files.pythonhosted.org/packages/56/e5/01106b9291ef1d680f82bc47d0c5b5e26dfed15b0754928e8f856c82c881/numpy-2.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369", size = 12609010 }, + { url = "https://files.pythonhosted.org/packages/9f/30/f23d9876de0f08dceb707c4dcf7f8dd7588266745029debb12a3cdd40be6/numpy-2.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391", size = 20924451 }, + { url = "https://files.pythonhosted.org/packages/6a/ec/6ea85b2da9d5dfa1dbb4cb3c76587fc8ddcae580cb1262303ab21c0926c4/numpy-2.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39", size = 14122390 }, + { url = "https://files.pythonhosted.org/packages/68/05/bfbdf490414a7dbaf65b10c78bc243f312c4553234b6d91c94eb7c4b53c2/numpy-2.2.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317", size = 5156590 }, + { url = "https://files.pythonhosted.org/packages/f7/ec/fe2e91b2642b9d6544518388a441bcd65c904cea38d9ff998e2e8ebf808e/numpy-2.2.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49", size = 6671958 }, + { url = "https://files.pythonhosted.org/packages/b1/6f/6531a78e182f194d33ee17e59d67d03d0d5a1ce7f6be7343787828d1bd4a/numpy-2.2.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2", size = 14019950 }, + { url = "https://files.pythonhosted.org/packages/e1/fb/13c58591d0b6294a08cc40fcc6b9552d239d773d520858ae27f39997f2ae/numpy-2.2.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7", size = 16079759 }, + { url = "https://files.pythonhosted.org/packages/2c/f2/f2f8edd62abb4b289f65a7f6d1f3650273af00b91b7267a2431be7f1aec6/numpy-2.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb", size = 15226139 }, + { url = "https://files.pythonhosted.org/packages/aa/29/14a177f1a90b8ad8a592ca32124ac06af5eff32889874e53a308f850290f/numpy-2.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648", size = 17856316 }, + { url = "https://files.pythonhosted.org/packages/95/03/242ae8d7b97f4e0e4ab8dd51231465fb23ed5e802680d629149722e3faf1/numpy-2.2.2-cp313-cp313t-win32.whl", hash = "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4", size = 6329134 }, + { url = "https://files.pythonhosted.org/packages/80/94/cd9e9b04012c015cb6320ab3bf43bc615e248dddfeb163728e800a5d96f0/numpy-2.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576", size = 12696208 }, +] + +[[package]] +name = "opentelemetry-api" +version = "1.29.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "importlib-metadata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/8e/b886a5e9861afa188d1fe671fb96ff9a1d90a23d57799331e137cc95d573/opentelemetry_api-1.29.0.tar.gz", hash = "sha256:d04a6cf78aad09614f52964ecb38021e248f5714dc32c2e0d8fd99517b4d69cf", size = 62900 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/53/5249ea860d417a26a3a6f1bdedfc0748c4f081a3adaec3d398bc0f7c6a71/opentelemetry_api-1.29.0-py3-none-any.whl", hash = "sha256:5fcd94c4141cc49c736271f3e1efb777bebe9cc535759c54c936cca4f1b312b8", size = 64304 }, +] + +[[package]] +name = "opentelemetry-sdk" +version = "1.29.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-semantic-conventions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0c/5a/1ed4c3cf6c09f80565fc085f7e8efa0c222712fd2a9412d07424705dcf72/opentelemetry_sdk-1.29.0.tar.gz", hash = "sha256:b0787ce6aade6ab84315302e72bd7a7f2f014b0fb1b7c3295b88afe014ed0643", size = 157229 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/1d/512b86af21795fb463726665e2f61db77d384e8779fdcf4cb0ceec47866d/opentelemetry_sdk-1.29.0-py3-none-any.whl", hash = "sha256:173be3b5d3f8f7d671f20ea37056710217959e774e2749d984355d1f9391a30a", size = 118078 }, +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.50b0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "opentelemetry-api" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/4e/d7c7c91ff47cd96fe4095dd7231701aec7347426fd66872ff320d6cd1fcc/opentelemetry_semantic_conventions-0.50b0.tar.gz", hash = "sha256:02dc6dbcb62f082de9b877ff19a3f1ffaa3c306300fa53bfac761c4567c83d38", size = 100459 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/fb/dc15fad105450a015e913cfa4f5c27b6a5f1bea8fb649f8cae11e699c8af/opentelemetry_semantic_conventions-0.50b0-py3-none-any.whl", hash = "sha256:e87efba8fdb67fb38113efea6a349531e75ed7ffc01562f65b802fcecb5e115e", size = 166602 }, +] + +[[package]] +name = "overrides" +version = "7.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pandocfilters" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663 }, +] + +[[package]] +name = "parso" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "prometheus-client" +version = "0.21.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/62/14/7d0f567991f3a9af8d1cd4f619040c93b68f09a02b6d0b6ab1b2d1ded5fe/prometheus_client-0.21.1.tar.gz", hash = "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb", size = 78551 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/c2/ab7d37426c179ceb9aeb109a85cda8948bb269b7561a0be870cc656eefe4/prometheus_client-0.21.1-py3-none-any.whl", hash = "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301", size = 54682 }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.50" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/e1/bd15cb8ffdcfeeb2bdc215de3c3cffca11408d829e4b8416dcfe71ba8854/prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab", size = 429087 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/ea/d836f008d33151c7a1f62caf3d8dd782e4d15f6a43897f64480c2b8de2ad/prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198", size = 387816 }, +] + +[[package]] +name = "proto-plus" +version = "1.25.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/05/74417b2061e1bf1b82776037cad97094228fa1c1b6e82d08a78d3fb6ddb6/proto_plus-1.25.0.tar.gz", hash = "sha256:fbb17f57f7bd05a68b7707e745e26528b0b3c34e378db91eef93912c54982d91", size = 56124 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/25/0b7cc838ae3d76d46539020ec39fc92bfc9acc29367e58fe912702c2a79e/proto_plus-1.25.0-py3-none-any.whl", hash = "sha256:c91fc4a65074ade8e458e95ef8bac34d4008daa7cce4a12d6707066fca648961", size = 50126 }, +] + +[[package]] +name = "protobuf" +version = "5.29.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/d1/e0a911544ca9993e0f17ce6d3cc0932752356c1b0a834397f28e63479344/protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620", size = 424945 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/7a/1e38f3cafa022f477ca0f57a1f49962f21ad25850c3ca0acd3b9d0091518/protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888", size = 422708 }, + { url = "https://files.pythonhosted.org/packages/61/fa/aae8e10512b83de633f2646506a6d835b151edf4b30d18d73afd01447253/protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a", size = 434508 }, + { url = "https://files.pythonhosted.org/packages/dd/04/3eaedc2ba17a088961d0e3bd396eac764450f431621b58a04ce898acd126/protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e", size = 417825 }, + { url = "https://files.pythonhosted.org/packages/4f/06/7c467744d23c3979ce250397e26d8ad8eeb2bea7b18ca12ad58313c1b8d5/protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84", size = 319573 }, + { url = "https://files.pythonhosted.org/packages/a8/45/2ebbde52ad2be18d3675b6bee50e68cd73c9e0654de77d595540b5129df8/protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f", size = 319672 }, + { url = "https://files.pythonhosted.org/packages/fd/b2/ab07b09e0f6d143dfb839693aa05765257bceaa13d03bf1a696b78323e7a/protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f", size = 172550 }, +] + +[[package]] +name = "psutil" +version = "6.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/5a/07871137bb752428aa4b659f910b399ba6f291156bdea939be3e96cae7cb/psutil-6.1.1.tar.gz", hash = "sha256:cf8496728c18f2d0b45198f06895be52f36611711746b7f30c464b422b50e2f5", size = 508502 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/99/ca79d302be46f7bdd8321089762dd4476ee725fce16fc2b2e1dbba8cac17/psutil-6.1.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:fc0ed7fe2231a444fc219b9c42d0376e0a9a1a72f16c5cfa0f68d19f1a0663e8", size = 247511 }, + { url = "https://files.pythonhosted.org/packages/0b/6b/73dbde0dd38f3782905d4587049b9be64d76671042fdcaf60e2430c6796d/psutil-6.1.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0bdd4eab935276290ad3cb718e9809412895ca6b5b334f5a9111ee6d9aff9377", size = 248985 }, + { url = "https://files.pythonhosted.org/packages/17/38/c319d31a1d3f88c5b79c68b3116c129e5133f1822157dd6da34043e32ed6/psutil-6.1.1-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6e06c20c05fe95a3d7302d74e7097756d4ba1247975ad6905441ae1b5b66003", size = 284488 }, + { url = "https://files.pythonhosted.org/packages/9c/39/0f88a830a1c8a3aba27fededc642da37613c57cbff143412e3536f89784f/psutil-6.1.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97f7cb9921fbec4904f522d972f0c0e1f4fabbdd4e0287813b21215074a0f160", size = 287477 }, + { url = "https://files.pythonhosted.org/packages/47/da/99f4345d4ddf2845cb5b5bd0d93d554e84542d116934fde07a0c50bd4e9f/psutil-6.1.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33431e84fee02bc84ea36d9e2c4a6d395d479c9dd9bba2376c1f6ee8f3a4e0b3", size = 289017 }, + { url = "https://files.pythonhosted.org/packages/38/53/bd755c2896f4461fd4f36fa6a6dcb66a88a9e4b9fd4e5b66a77cf9d4a584/psutil-6.1.1-cp37-abi3-win32.whl", hash = "sha256:eaa912e0b11848c4d9279a93d7e2783df352b082f40111e078388701fd479e53", size = 250602 }, + { url = "https://files.pythonhosted.org/packages/7b/d7/7831438e6c3ebbfa6e01a927127a6cb42ad3ab844247f3c5b96bea25d73d/psutil-6.1.1-cp37-abi3-win_amd64.whl", hash = "sha256:f35cfccb065fff93529d2afb4a2e89e363fe63ca1e4a5da22b603a85833c2649", size = 254444 }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, +] + +[[package]] +name = "pyasn1" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/67/6afbf0d507f73c32d21084a79946bfcfca5fbc62a72057e9c23797a737c9/pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c", size = 310028 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/89/bc88a6711935ba795a679ea6ebee07e128050d6382eaa35a0a47c8032bdc/pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd", size = 181537 }, +] + +[[package]] +name = "pycparser" +version = "2.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, +] + +[[package]] +name = "pydantic" +version = "2.10.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/c7/ca334c2ef6f2e046b1144fe4bb2a5da8a4c574e7f2ebf7e16b34a6a2fa92/pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff", size = 761287 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/26/82663c79010b28eddf29dcdd0ea723439535fa917fce5905885c0e9ba562/pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53", size = 431426 }, +] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 }, + { url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 }, + { url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 }, + { url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 }, + { url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 }, + { url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 }, + { url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 }, + { url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 }, + { url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 }, + { url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 }, + { url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 }, + { url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 }, + { url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 }, + { url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 }, + { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 }, + { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 }, + { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 }, + { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 }, + { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 }, + { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 }, + { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 }, + { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 }, + { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 }, + { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 }, + { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 }, + { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 }, + { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 }, + { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, +] + +[[package]] +name = "pytest" +version = "8.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, +] + +[[package]] +name = "pytest-asyncio" +version = "0.25.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/df/adcc0d60f1053d74717d21d58c0048479e9cab51464ce0d2965b086bd0e2/pytest_asyncio-0.25.2.tar.gz", hash = "sha256:3f8ef9a98f45948ea91a0ed3dc4268b5326c0e7bce73892acc654df4262ad45f", size = 53950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/d8/defa05ae50dcd6019a95527200d3b3980043df5aa445d40cb0ef9f7f98ab/pytest_asyncio-0.25.2-py3-none-any.whl", hash = "sha256:0d0bb693f7b99da304a0634afc0a4b19e49d5e0de2d670f38dc4bfa5727c5075", size = 19400 }, +] + +[[package]] +name = "pytest-cov" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "python-json-logger" +version = "3.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/c4/358cd13daa1d912ef795010897a483ab2f0b41c9ea1b35235a8b2f7d15a7/python_json_logger-3.2.1.tar.gz", hash = "sha256:8eb0554ea17cb75b05d2848bc14fb02fbdbd9d6972120781b974380bfa162008", size = 16287 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/72/2f30cf26664fcfa0bd8ec5ee62ec90c03bd485e4a294d92aabc76c5203a5/python_json_logger-3.2.1-py3-none-any.whl", hash = "sha256:cdc17047eb5374bd311e748b42f99d71223f3b0e186f4206cc5d52aefe85b090", size = 14924 }, +] + +[[package]] +name = "pywin32" +version = "308" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/7c/d00d6bdd96de4344e06c4afbf218bc86b54436a94c01c71a8701f613aa56/pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897", size = 5939729 }, + { url = "https://files.pythonhosted.org/packages/21/27/0c8811fbc3ca188f93b5354e7c286eb91f80a53afa4e11007ef661afa746/pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47", size = 6543015 }, + { url = "https://files.pythonhosted.org/packages/9d/0f/d40f8373608caed2255781a3ad9a51d03a594a1248cd632d6a298daca693/pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091", size = 7976033 }, + { url = "https://files.pythonhosted.org/packages/a9/a4/aa562d8935e3df5e49c161b427a3a2efad2ed4e9cf81c3de636f1fdddfd0/pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed", size = 5938579 }, + { url = "https://files.pythonhosted.org/packages/c7/50/b0efb8bb66210da67a53ab95fd7a98826a97ee21f1d22949863e6d588b22/pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4", size = 6542056 }, + { url = "https://files.pythonhosted.org/packages/26/df/2b63e3e4f2df0224f8aaf6d131f54fe4e8c96400eb9df563e2aae2e1a1f9/pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd", size = 7974986 }, +] + +[[package]] +name = "pywinpty" +version = "2.0.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/82/90f8750423cba4b9b6c842df227609fb60704482d7abf6dd47e2babc055a/pywinpty-2.0.14.tar.gz", hash = "sha256:18bd9529e4a5daf2d9719aa17788ba6013e594ae94c5a0c27e83df3278b0660e", size = 27769 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/79/759ae767a3b78d340446efd54dd1fe4f7dafa4fc7be96ed757e44bcdba54/pywinpty-2.0.14-cp312-none-win_amd64.whl", hash = "sha256:55dad362ef3e9408ade68fd173e4f9032b3ce08f68cfe7eacb2c263ea1179737", size = 1397207 }, + { url = "https://files.pythonhosted.org/packages/7d/34/b77b3c209bf2eaa6455390c8d5449241637f5957f41636a2204065d52bfa/pywinpty-2.0.14-cp313-none-win_amd64.whl", hash = "sha256:074fb988a56ec79ca90ed03a896d40707131897cefb8f76f926e3834227f2819", size = 1396698 }, +] + +[[package]] +name = "pyxdg" +version = "0.28" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/25/7998cd2dec731acbd438fbf91bc619603fc5188de0a9a17699a781840452/pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4", size = 77776 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/8d/cf41b66a8110670e3ad03dab9b759704eeed07fa96e90fdc0357b2ba70e2/pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab", size = 49520 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "pyzmq" +version = "26.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "implementation_name == 'pypy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/05/bed626b9f7bb2322cdbbf7b4bd8f54b1b617b0d2ab2d3547d6e39428a48e/pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f", size = 271975 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/2f/78a766c8913ad62b28581777ac4ede50c6d9f249d39c2963e279524a1bbe/pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9", size = 1343105 }, + { url = "https://files.pythonhosted.org/packages/b7/9c/4b1e2d3d4065be715e007fe063ec7885978fad285f87eae1436e6c3201f4/pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52", size = 1008365 }, + { url = "https://files.pythonhosted.org/packages/4f/ef/5a23ec689ff36d7625b38d121ef15abfc3631a9aecb417baf7a4245e4124/pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08", size = 665923 }, + { url = "https://files.pythonhosted.org/packages/ae/61/d436461a47437d63c6302c90724cf0981883ec57ceb6073873f32172d676/pyzmq-26.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5", size = 903400 }, + { url = "https://files.pythonhosted.org/packages/47/42/fc6d35ecefe1739a819afaf6f8e686f7f02a4dd241c78972d316f403474c/pyzmq-26.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae", size = 860034 }, + { url = "https://files.pythonhosted.org/packages/07/3b/44ea6266a6761e9eefaa37d98fabefa112328808ac41aa87b4bbb668af30/pyzmq-26.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711", size = 860579 }, + { url = "https://files.pythonhosted.org/packages/38/6f/4df2014ab553a6052b0e551b37da55166991510f9e1002c89cab7ce3b3f2/pyzmq-26.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6", size = 1196246 }, + { url = "https://files.pythonhosted.org/packages/38/9d/ee240fc0c9fe9817f0c9127a43238a3e28048795483c403cc10720ddef22/pyzmq-26.2.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3", size = 1507441 }, + { url = "https://files.pythonhosted.org/packages/85/4f/01711edaa58d535eac4a26c294c617c9a01f09857c0ce191fd574d06f359/pyzmq-26.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b", size = 1406498 }, + { url = "https://files.pythonhosted.org/packages/07/18/907134c85c7152f679ed744e73e645b365f3ad571f38bdb62e36f347699a/pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7", size = 575533 }, + { url = "https://files.pythonhosted.org/packages/ce/2c/a6f4a20202a4d3c582ad93f95ee78d79bbdc26803495aec2912b17dbbb6c/pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a", size = 637768 }, + { url = "https://files.pythonhosted.org/packages/5f/0e/eb16ff731632d30554bf5af4dbba3ffcd04518219d82028aea4ae1b02ca5/pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b", size = 540675 }, + { url = "https://files.pythonhosted.org/packages/04/a7/0f7e2f6c126fe6e62dbae0bc93b1bd3f1099cf7fea47a5468defebe3f39d/pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726", size = 1006564 }, + { url = "https://files.pythonhosted.org/packages/31/b6/a187165c852c5d49f826a690857684333a6a4a065af0a6015572d2284f6a/pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3", size = 1340447 }, + { url = "https://files.pythonhosted.org/packages/68/ba/f4280c58ff71f321602a6e24fd19879b7e79793fb8ab14027027c0fb58ef/pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50", size = 665485 }, + { url = "https://files.pythonhosted.org/packages/77/b5/c987a5c53c7d8704216f29fc3d810b32f156bcea488a940e330e1bcbb88d/pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb", size = 903484 }, + { url = "https://files.pythonhosted.org/packages/29/c9/07da157d2db18c72a7eccef8e684cefc155b712a88e3d479d930aa9eceba/pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187", size = 859981 }, + { url = "https://files.pythonhosted.org/packages/43/09/e12501bd0b8394b7d02c41efd35c537a1988da67fc9c745cae9c6c776d31/pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b", size = 860334 }, + { url = "https://files.pythonhosted.org/packages/eb/ff/f5ec1d455f8f7385cc0a8b2acd8c807d7fade875c14c44b85c1bddabae21/pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18", size = 1196179 }, + { url = "https://files.pythonhosted.org/packages/ec/8a/bb2ac43295b1950fe436a81fc5b298be0b96ac76fb029b514d3ed58f7b27/pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115", size = 1507668 }, + { url = "https://files.pythonhosted.org/packages/a9/49/dbc284ebcfd2dca23f6349227ff1616a7ee2c4a35fe0a5d6c3deff2b4fed/pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e", size = 1406539 }, + { url = "https://files.pythonhosted.org/packages/00/68/093cdce3fe31e30a341d8e52a1ad86392e13c57970d722c1f62a1d1a54b6/pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5", size = 575567 }, + { url = "https://files.pythonhosted.org/packages/92/ae/6cc4657148143412b5819b05e362ae7dd09fb9fe76e2a539dcff3d0386bc/pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad", size = 637551 }, + { url = "https://files.pythonhosted.org/packages/6c/67/fbff102e201688f97c8092e4c3445d1c1068c2f27bbd45a578df97ed5f94/pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797", size = 540378 }, + { url = "https://files.pythonhosted.org/packages/3f/fe/2d998380b6e0122c6c4bdf9b6caf490831e5f5e2d08a203b5adff060c226/pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a", size = 1007378 }, + { url = "https://files.pythonhosted.org/packages/4a/f4/30d6e7157f12b3a0390bde94d6a8567cdb88846ed068a6e17238a4ccf600/pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc", size = 1329532 }, + { url = "https://files.pythonhosted.org/packages/82/86/3fe917870e15ee1c3ad48229a2a64458e36036e64b4afa9659045d82bfa8/pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5", size = 653242 }, + { url = "https://files.pythonhosted.org/packages/50/2d/242e7e6ef6c8c19e6cb52d095834508cd581ffb925699fd3c640cdc758f1/pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672", size = 888404 }, + { url = "https://files.pythonhosted.org/packages/ac/11/7270566e1f31e4ea73c81ec821a4b1688fd551009a3d2bab11ec66cb1e8f/pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797", size = 845858 }, + { url = "https://files.pythonhosted.org/packages/91/d5/72b38fbc69867795c8711bdd735312f9fef1e3d9204e2f63ab57085434b9/pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386", size = 847375 }, + { url = "https://files.pythonhosted.org/packages/dd/9a/10ed3c7f72b4c24e719c59359fbadd1a27556a28b36cdf1cd9e4fb7845d5/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306", size = 1183489 }, + { url = "https://files.pythonhosted.org/packages/72/2d/8660892543fabf1fe41861efa222455811adac9f3c0818d6c3170a1153e3/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6", size = 1492932 }, + { url = "https://files.pythonhosted.org/packages/7b/d6/32fd69744afb53995619bc5effa2a405ae0d343cd3e747d0fbc43fe894ee/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0", size = 1392485 }, +] + +[[package]] +name = "referencing" +version = "0.36.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/32/fd98246df7a0f309b58cae68b10b6b219ef2eb66747f00dfb34422687087/referencing-0.36.1.tar.gz", hash = "sha256:ca2e6492769e3602957e9b831b94211599d2aade9477f5d44110d2530cf9aade", size = 74661 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/fa/9f193ef0c9074b659009f06d7cbacc6f25b072044815bcf799b76533dbb8/referencing-0.36.1-py3-none-any.whl", hash = "sha256:363d9c65f080d0d70bc41c721dce3c7f3e77fc09f269cd5c8813da18069a6794", size = 26777 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490 }, +] + +[[package]] +name = "rfc3986-validator" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242 }, +] + +[[package]] +name = "rpds-py" +version = "0.22.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/80/cce854d0921ff2f0a9fa831ba3ad3c65cee3a46711addf39a2af52df2cfd/rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d", size = 26771 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/47/3383ee3bd787a2a5e65a9b9edc37ccf8505c0a00170e3a5e6ea5fbcd97f7/rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e", size = 352334 }, + { url = "https://files.pythonhosted.org/packages/40/14/aa6400fa8158b90a5a250a77f2077c0d0cd8a76fce31d9f2b289f04c6dec/rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56", size = 342111 }, + { url = "https://files.pythonhosted.org/packages/7d/06/395a13bfaa8a28b302fb433fb285a67ce0ea2004959a027aea8f9c52bad4/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45", size = 384286 }, + { url = "https://files.pythonhosted.org/packages/43/52/d8eeaffab047e6b7b7ef7f00d5ead074a07973968ffa2d5820fa131d7852/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e", size = 391739 }, + { url = "https://files.pythonhosted.org/packages/83/31/52dc4bde85c60b63719610ed6f6d61877effdb5113a72007679b786377b8/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d", size = 427306 }, + { url = "https://files.pythonhosted.org/packages/70/d5/1bab8e389c2261dba1764e9e793ed6830a63f830fdbec581a242c7c46bda/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38", size = 442717 }, + { url = "https://files.pythonhosted.org/packages/82/a1/a45f3e30835b553379b3a56ea6c4eb622cf11e72008229af840e4596a8ea/rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15", size = 385721 }, + { url = "https://files.pythonhosted.org/packages/a6/27/780c942de3120bdd4d0e69583f9c96e179dfff082f6ecbb46b8d6488841f/rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059", size = 415824 }, + { url = "https://files.pythonhosted.org/packages/94/0b/aa0542ca88ad20ea719b06520f925bae348ea5c1fdf201b7e7202d20871d/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e", size = 561227 }, + { url = "https://files.pythonhosted.org/packages/0d/92/3ed77d215f82c8f844d7f98929d56cc321bb0bcfaf8f166559b8ec56e5f1/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61", size = 587424 }, + { url = "https://files.pythonhosted.org/packages/09/42/cacaeb047a22cab6241f107644f230e2935d4efecf6488859a7dd82fc47d/rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7", size = 555953 }, + { url = "https://files.pythonhosted.org/packages/e6/52/c921dc6d5f5d45b212a456c1f5b17df1a471127e8037eb0972379e39dff4/rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627", size = 221339 }, + { url = "https://files.pythonhosted.org/packages/f2/c7/f82b5be1e8456600395366f86104d1bd8d0faed3802ad511ef6d60c30d98/rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4", size = 235786 }, + { url = "https://files.pythonhosted.org/packages/d0/bf/36d5cc1f2c609ae6e8bf0fc35949355ca9d8790eceb66e6385680c951e60/rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84", size = 351657 }, + { url = "https://files.pythonhosted.org/packages/24/2a/f1e0fa124e300c26ea9382e59b2d582cba71cedd340f32d1447f4f29fa4e/rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25", size = 341829 }, + { url = "https://files.pythonhosted.org/packages/cf/c2/0da1231dd16953845bed60d1a586fcd6b15ceaeb965f4d35cdc71f70f606/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4", size = 384220 }, + { url = "https://files.pythonhosted.org/packages/c7/73/a4407f4e3a00a9d4b68c532bf2d873d6b562854a8eaff8faa6133b3588ec/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5", size = 391009 }, + { url = "https://files.pythonhosted.org/packages/a9/c3/04b7353477ab360fe2563f5f0b176d2105982f97cd9ae80a9c5a18f1ae0f/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc", size = 426989 }, + { url = "https://files.pythonhosted.org/packages/8d/e6/e4b85b722bcf11398e17d59c0f6049d19cd606d35363221951e6d625fcb0/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b", size = 441544 }, + { url = "https://files.pythonhosted.org/packages/27/fc/403e65e56f65fff25f2973216974976d3f0a5c3f30e53758589b6dc9b79b/rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518", size = 385179 }, + { url = "https://files.pythonhosted.org/packages/57/9b/2be9ff9700d664d51fd96b33d6595791c496d2778cb0b2a634f048437a55/rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd", size = 415103 }, + { url = "https://files.pythonhosted.org/packages/bb/a5/03c2ad8ca10994fcf22dd2150dd1d653bc974fa82d9a590494c84c10c641/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2", size = 560916 }, + { url = "https://files.pythonhosted.org/packages/ba/2e/be4fdfc8b5b576e588782b56978c5b702c5a2307024120d8aeec1ab818f0/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16", size = 587062 }, + { url = "https://files.pythonhosted.org/packages/67/e0/2034c221937709bf9c542603d25ad43a68b4b0a9a0c0b06a742f2756eb66/rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f", size = 555734 }, + { url = "https://files.pythonhosted.org/packages/ea/ce/240bae07b5401a22482b58e18cfbabaa392409b2797da60223cca10d7367/rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de", size = 220663 }, + { url = "https://files.pythonhosted.org/packages/cb/f0/d330d08f51126330467edae2fa4efa5cec8923c87551a79299380fdea30d/rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9", size = 235503 }, + { url = "https://files.pythonhosted.org/packages/f7/c4/dbe1cc03df013bf2feb5ad00615038050e7859f381e96fb5b7b4572cd814/rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b", size = 347698 }, + { url = "https://files.pythonhosted.org/packages/a4/3a/684f66dd6b0f37499cad24cd1c0e523541fd768576fa5ce2d0a8799c3cba/rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b", size = 337330 }, + { url = "https://files.pythonhosted.org/packages/82/eb/e022c08c2ce2e8f7683baa313476492c0e2c1ca97227fe8a75d9f0181e95/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1", size = 380022 }, + { url = "https://files.pythonhosted.org/packages/e4/21/5a80e653e4c86aeb28eb4fea4add1f72e1787a3299687a9187105c3ee966/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83", size = 390754 }, + { url = "https://files.pythonhosted.org/packages/37/a4/d320a04ae90f72d080b3d74597074e62be0a8ecad7d7321312dfe2dc5a6a/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd", size = 423840 }, + { url = "https://files.pythonhosted.org/packages/87/70/674dc47d93db30a6624279284e5631be4c3a12a0340e8e4f349153546728/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1", size = 438970 }, + { url = "https://files.pythonhosted.org/packages/3f/64/9500f4d66601d55cadd21e90784cfd5d5f4560e129d72e4339823129171c/rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3", size = 383146 }, + { url = "https://files.pythonhosted.org/packages/4d/45/630327addb1d17173adcf4af01336fd0ee030c04798027dfcb50106001e0/rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130", size = 408294 }, + { url = "https://files.pythonhosted.org/packages/5f/ef/8efb3373cee54ea9d9980b772e5690a0c9e9214045a4e7fa35046e399fee/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c", size = 556345 }, + { url = "https://files.pythonhosted.org/packages/54/01/151d3b9ef4925fc8f15bfb131086c12ec3c3d6dd4a4f7589c335bf8e85ba/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b", size = 582292 }, + { url = "https://files.pythonhosted.org/packages/30/89/35fc7a6cdf3477d441c7aca5e9bbf5a14e0f25152aed7f63f4e0b141045d/rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333", size = 553855 }, + { url = "https://files.pythonhosted.org/packages/8f/e0/830c02b2457c4bd20a8c5bb394d31d81f57fbefce2dbdd2e31feff4f7003/rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730", size = 219100 }, + { url = "https://files.pythonhosted.org/packages/f8/30/7ac943f69855c2db77407ae363484b915d861702dbba1aa82d68d57f42be/rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf", size = 233794 }, +] + +[[package]] +name = "rsa" +version = "4.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/65/7d973b89c4d2351d7fb232c2e452547ddfa243e93131e7cfa766da627b52/rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21", size = 29711 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/97/fa78e3d2f65c02c8e1268b9aba606569fe97f6c8f7c2d74394553347c145/rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", size = 34315 }, +] + +[[package]] +name = "ruff" +version = "0.9.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/63/77ecca9d21177600f551d1c58ab0e5a0b260940ea7312195bd2a4798f8a8/ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0", size = 3553799 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/b9/0e168e4e7fb3af851f739e8f07889b91d1a33a30fca8c29fa3149d6b03ec/ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347", size = 11652408 }, + { url = "https://files.pythonhosted.org/packages/2c/22/08ede5db17cf701372a461d1cb8fdde037da1d4fa622b69ac21960e6237e/ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00", size = 11587553 }, + { url = "https://files.pythonhosted.org/packages/42/05/dedfc70f0bf010230229e33dec6e7b2235b2a1b8cbb2a991c710743e343f/ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4", size = 11020755 }, + { url = "https://files.pythonhosted.org/packages/df/9b/65d87ad9b2e3def67342830bd1af98803af731243da1255537ddb8f22209/ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d", size = 11826502 }, + { url = "https://files.pythonhosted.org/packages/93/02/f2239f56786479e1a89c3da9bc9391120057fc6f4a8266a5b091314e72ce/ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c", size = 11390562 }, + { url = "https://files.pythonhosted.org/packages/c9/37/d3a854dba9931f8cb1b2a19509bfe59e00875f48ade632e95aefcb7a0aee/ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f", size = 12548968 }, + { url = "https://files.pythonhosted.org/packages/fa/c3/c7b812bb256c7a1d5553433e95980934ffa85396d332401f6b391d3c4569/ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684", size = 13187155 }, + { url = "https://files.pythonhosted.org/packages/bd/5a/3c7f9696a7875522b66aa9bba9e326e4e5894b4366bd1dc32aa6791cb1ff/ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d", size = 12704674 }, + { url = "https://files.pythonhosted.org/packages/be/d6/d908762257a96ce5912187ae9ae86792e677ca4f3dc973b71e7508ff6282/ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df", size = 14529328 }, + { url = "https://files.pythonhosted.org/packages/2d/c2/049f1e6755d12d9cd8823242fa105968f34ee4c669d04cac8cea51a50407/ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247", size = 12385955 }, + { url = "https://files.pythonhosted.org/packages/91/5a/a9bdb50e39810bd9627074e42743b00e6dc4009d42ae9f9351bc3dbc28e7/ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e", size = 11810149 }, + { url = "https://files.pythonhosted.org/packages/e5/fd/57df1a0543182f79a1236e82a79c68ce210efb00e97c30657d5bdb12b478/ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe", size = 11479141 }, + { url = "https://files.pythonhosted.org/packages/dc/16/bc3fd1d38974f6775fc152a0554f8c210ff80f2764b43777163c3c45d61b/ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb", size = 12014073 }, + { url = "https://files.pythonhosted.org/packages/47/6b/e4ca048a8f2047eb652e1e8c755f384d1b7944f69ed69066a37acd4118b0/ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a", size = 12435758 }, + { url = "https://files.pythonhosted.org/packages/c2/40/4d3d6c979c67ba24cf183d29f706051a53c36d78358036a9cd21421582ab/ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145", size = 9796916 }, + { url = "https://files.pythonhosted.org/packages/c3/ef/7f548752bdb6867e6939489c87fe4da489ab36191525fadc5cede2a6e8e2/ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5", size = 10773080 }, + { url = "https://files.pythonhosted.org/packages/0e/4e/33df635528292bd2d18404e4daabcd74ca8a9853b2e1df85ed3d32d24362/ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6", size = 10001738 }, +] + +[[package]] +name = "send2trash" +version = "1.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/3a/aec9b02217bb79b87bbc1a21bc6abc51e3d5dcf65c30487ac96c0908c722/Send2Trash-1.8.3.tar.gz", hash = "sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf", size = 17394 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/b0/4562db6223154aa4e22f939003cb92514c79f3d4dccca3444253fd17f902/Send2Trash-1.8.3-py3-none-any.whl", hash = "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9", size = 18072 }, +] + +[[package]] +name = "setuptools" +version = "75.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ec/089608b791d210aec4e7f97488e67ab0d33add3efccb83a056cbafe3a2a6/setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6", size = 1343222 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3", size = 1228782 }, +] + +[[package]] +name = "shapely" +version = "2.0.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4a/89/0d20bac88016be35ff7d3c0c2ae64b477908f1b1dfa540c5d69ac7af07fe/shapely-2.0.6.tar.gz", hash = "sha256:997f6159b1484059ec239cacaa53467fd8b5564dabe186cd84ac2944663b0bf6", size = 282361 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/77/efd9f9d4b6a762f976f8b082f54c9be16f63050389500fb52e4f6cc07c1a/shapely-2.0.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cec9193519940e9d1b86a3b4f5af9eb6910197d24af02f247afbfb47bcb3fab0", size = 1450326 }, + { url = "https://files.pythonhosted.org/packages/68/53/5efa6e7a4036a94fe6276cf7bbb298afded51ca3396b03981ad680c8cc7d/shapely-2.0.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83b94a44ab04a90e88be69e7ddcc6f332da7c0a0ebb1156e1c4f568bbec983c3", size = 1298480 }, + { url = "https://files.pythonhosted.org/packages/88/a2/1be1db4fc262e536465a52d4f19d85834724fedf2299a1b9836bc82fe8fa/shapely-2.0.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:537c4b2716d22c92036d00b34aac9d3775e3691f80c7aa517c2c290351f42cd8", size = 2439311 }, + { url = "https://files.pythonhosted.org/packages/d5/7d/9a57e187cbf2fbbbdfd4044a4f9ce141c8d221f9963750d3b001f0ec080d/shapely-2.0.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fea108334be345c283ce74bf064fa00cfdd718048a8af7343c59eb40f59726", size = 2524835 }, + { url = "https://files.pythonhosted.org/packages/6d/0a/f407509ab56825f39bf8cfce1fb410238da96cf096809c3e404e5bc71ea1/shapely-2.0.6-cp312-cp312-win32.whl", hash = "sha256:42fd4cd4834747e4990227e4cbafb02242c0cffe9ce7ef9971f53ac52d80d55f", size = 1295613 }, + { url = "https://files.pythonhosted.org/packages/7b/b3/857afd9dfbfc554f10d683ac412eac6fa260d1f4cd2967ecb655c57e831a/shapely-2.0.6-cp312-cp312-win_amd64.whl", hash = "sha256:665990c84aece05efb68a21b3523a6b2057e84a1afbef426ad287f0796ef8a48", size = 1442539 }, + { url = "https://files.pythonhosted.org/packages/34/e8/d164ef5b0eab86088cde06dee8415519ffd5bb0dd1bd9d021e640e64237c/shapely-2.0.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:42805ef90783ce689a4dde2b6b2f261e2c52609226a0438d882e3ced40bb3013", size = 1445344 }, + { url = "https://files.pythonhosted.org/packages/ce/e2/9fba7ac142f7831757a10852bfa465683724eadbc93d2d46f74a16f9af04/shapely-2.0.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6d2cb146191a47bd0cee8ff5f90b47547b82b6345c0d02dd8b25b88b68af62d7", size = 1296182 }, + { url = "https://files.pythonhosted.org/packages/cf/dc/790d4bda27d196cd56ec66975eaae3351c65614cafd0e16ddde39ec9fb92/shapely-2.0.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3fdef0a1794a8fe70dc1f514440aa34426cc0ae98d9a1027fb299d45741c381", size = 2423426 }, + { url = "https://files.pythonhosted.org/packages/af/b0/f8169f77eac7392d41e231911e0095eb1148b4d40c50ea9e34d999c89a7e/shapely-2.0.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c665a0301c645615a107ff7f52adafa2153beab51daf34587170d85e8ba6805", size = 2513249 }, + { url = "https://files.pythonhosted.org/packages/f6/1d/a8c0e9ab49ff2f8e4dedd71b0122eafb22a18ad7e9d256025e1f10c84704/shapely-2.0.6-cp313-cp313-win32.whl", hash = "sha256:0334bd51828f68cd54b87d80b3e7cee93f249d82ae55a0faf3ea21c9be7b323a", size = 1294848 }, + { url = "https://files.pythonhosted.org/packages/23/38/2bc32dd1e7e67a471d4c60971e66df0bdace88656c47a9a728ace0091075/shapely-2.0.6-cp313-cp313-win_amd64.whl", hash = "sha256:d37d070da9e0e0f0a530a621e17c0b8c3c9d04105655132a87cfff8bd77cc4c2", size = 1441371 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "soupsieve" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, +] + +[[package]] +name = "terminado" +version = "0.18.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess", marker = "os_name != 'nt'" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "tornado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154 }, +] + +[[package]] +name = "tinycss2" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "webencodings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610 }, +] + +[[package]] +name = "tornado" +version = "6.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/45/a0daf161f7d6f36c3ea5fc0c2de619746cc3dd4c76402e9db545bd920f63/tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b", size = 501135 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/7e/71f604d8cea1b58f82ba3590290b66da1e72d840aeb37e0d5f7291bd30db/tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1", size = 436299 }, + { url = "https://files.pythonhosted.org/packages/96/44/87543a3b99016d0bf54fdaab30d24bf0af2e848f1d13d34a3a5380aabe16/tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803", size = 434253 }, + { url = "https://files.pythonhosted.org/packages/cb/fb/fdf679b4ce51bcb7210801ef4f11fdac96e9885daa402861751353beea6e/tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec", size = 437602 }, + { url = "https://files.pythonhosted.org/packages/4f/3b/e31aeffffc22b475a64dbeb273026a21b5b566f74dee48742817626c47dc/tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946", size = 436972 }, + { url = "https://files.pythonhosted.org/packages/22/55/b78a464de78051a30599ceb6983b01d8f732e6f69bf37b4ed07f642ac0fc/tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf", size = 437173 }, + { url = "https://files.pythonhosted.org/packages/79/5e/be4fb0d1684eb822c9a62fb18a3e44a06188f78aa466b2ad991d2ee31104/tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634", size = 437892 }, + { url = "https://files.pythonhosted.org/packages/f5/33/4f91fdd94ea36e1d796147003b490fe60a0215ac5737b6f9c65e160d4fe0/tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73", size = 437334 }, + { url = "https://files.pythonhosted.org/packages/2b/ae/c1b22d4524b0e10da2f29a176fb2890386f7bd1f63aacf186444873a88a0/tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c", size = 437261 }, + { url = "https://files.pythonhosted.org/packages/b5/25/36dbd49ab6d179bcfc4c6c093a51795a4f3bed380543a8242ac3517a1751/tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482", size = 438463 }, + { url = "https://files.pythonhosted.org/packages/61/cc/58b1adeb1bb46228442081e746fcdbc4540905c87e8add7c277540934edb/tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38", size = 438907 }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20241206" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/60/47d92293d9bc521cd2301e423a358abfac0ad409b3a1606d8fbae1321961/types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb", size = 13802 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/b3/ca41df24db5eb99b00d97f89d7674a90cb6b3134c52fb8121b6d8d30f15c/types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53", size = 14384 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "uri-template" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140 }, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, +] + +[[package]] +name = "webcolors" +version = "24.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/29/061ec845fb58521848f3739e466efd8250b4b7b98c1b6c5bf4d40b419b7e/webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6", size = 45064 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/e8/c0e05e4684d13459f93d312077a9a2efbe04d59c393bc2b8802248c908d4/webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9", size = 14934 }, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774 }, +] + +[[package]] +name = "websocket-client" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826 }, +] + +[[package]] +name = "widgetsnbextension" +version = "4.0.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/fc/238c424fd7f4ebb25f8b1da9a934a3ad7c848286732ae04263661eb0fc03/widgetsnbextension-4.0.13.tar.gz", hash = "sha256:ffcb67bc9febd10234a362795f643927f4e0c05d9342c727b65d2384f8feacb6", size = 1164730 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/02/88b65cc394961a60c43c70517066b6b679738caf78506a5da7b88ffcb643/widgetsnbextension-4.0.13-py3-none-any.whl", hash = "sha256:74b2692e8500525cc38c2b877236ba51d34541e6385eeed5aec15a70f88a6c71", size = 2335872 }, +] + +[[package]] +name = "wrapt" +version = "1.17.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799 }, + { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821 }, + { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919 }, + { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721 }, + { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899 }, + { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222 }, + { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707 }, + { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685 }, + { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567 }, + { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672 }, + { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865 }, + { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800 }, + { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824 }, + { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920 }, + { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690 }, + { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861 }, + { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174 }, + { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721 }, + { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763 }, + { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585 }, + { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676 }, + { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871 }, + { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312 }, + { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062 }, + { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155 }, + { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471 }, + { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208 }, + { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339 }, + { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232 }, + { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476 }, + { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 }, + { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 }, + { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 }, + { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 }, +] + +[[package]] +name = "zipp" +version = "3.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, +] From f6696daad4df7ea599b417d380cf2ad46d359a16 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 29 Jan 2025 10:58:09 -0800 Subject: [PATCH 363/562] fix(js)!: client streamFlow returns properties, not functions (#1690) Reconciles the server & client SDK to return a promise and async iterable rather than a function that returns these types. Docs already suggested we did this. --- js/core/src/async.ts | 3 ++ js/genkit/src/client/client.ts | 46 +++++-------------- js/plugins/express/README.md | 4 +- js/plugins/express/tests/express_test.ts | 12 ++--- js/plugins/firebase/tests/functions_test.ts | 4 +- .../app/samples/chatbot/chatbot.component.ts | 4 +- .../app/samples/chatbot/chatbot.component.ts | 2 +- .../streaming-json.component.ts | 4 +- tests/src/flow_server_test.ts | 4 +- 9 files changed, 32 insertions(+), 51 deletions(-) diff --git a/js/core/src/async.ts b/js/core/src/async.ts index 84c872b09..1c42d8ead 100644 --- a/js/core/src/async.ts +++ b/js/core/src/async.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +// NOTE: This file is pulled into client code and cannot have any Node-only +// dependencies. + /** * A handle to a promise and its resolvers. */ diff --git a/js/genkit/src/client/client.ts b/js/genkit/src/client/client.ts index 99ba6e6e2..6caeb9096 100644 --- a/js/genkit/src/client/client.ts +++ b/js/genkit/src/client/client.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { Channel } from '@genkit-ai/core/async'; + const __flowStreamDelimiter = '\n\n'; /** @@ -43,49 +45,25 @@ export function streamFlow({ input?: any; headers?: Record; }): { - output(): Promise; - stream(): AsyncIterable; + readonly output: Promise; + readonly stream: AsyncIterable; } { - let chunkStreamController: ReadableStreamDefaultController | undefined = - undefined; - const chunkStream = new ReadableStream({ - start(controller) { - chunkStreamController = controller; - }, - pull() {}, - cancel() {}, - }); + const channel = new Channel(); const operationPromise = __flowRunEnvelope({ url, input, - streamingCallback: (c) => { - chunkStreamController?.enqueue(c); - }, + streamingCallback: (c) => channel.send(c), headers, }); - operationPromise.then((o) => { - chunkStreamController?.close(); - return o; - }); + operationPromise.then( + () => channel.close(), + (err) => channel.error(err) + ); return { - output() { - return operationPromise; - }, - async *stream() { - const reader = chunkStream.getReader(); - while (true) { - const chunk = await reader.read(); - if (chunk?.value !== undefined) { - yield chunk.value; - } - if (chunk.done) { - break; - } - } - return await operationPromise; - }, + output: operationPromise, + stream: channel, }; } diff --git a/js/plugins/express/README.md b/js/plugins/express/README.md index 8c41248e9..f50bacd4e 100644 --- a/js/plugins/express/README.md +++ b/js/plugins/express/README.md @@ -80,10 +80,10 @@ const result = streamFlow({ url: `http://localhost:${port}/simpleFlow`, input: 'say hello', }); -for await (const chunk of result.stream()) { +for await (const chunk of result.stream) { console.log(chunk); } -console.log(await result.output()); +console.log(await result.output); ``` The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. diff --git a/js/plugins/express/tests/express_test.ts b/js/plugins/express/tests/express_test.ts index d4c0917fb..0867809b4 100644 --- a/js/plugins/express/tests/express_test.ts +++ b/js/plugins/express/tests/express_test.ts @@ -283,7 +283,7 @@ describe('expressHandler', async () => { }); const gotChunks: GenerateResponseChunkData[] = []; - for await (const chunk of result.stream()) { + for await (const chunk of result.stream) { gotChunks.push(chunk); } @@ -293,7 +293,7 @@ describe('expressHandler', async () => { { index: 0, role: 'model', content: [{ text: '1' }] }, ]); - assert.strictEqual(await result.output(), 'Echo: olleh'); + assert.strictEqual(await result.output, 'Echo: olleh'); }); it('stream a model', async () => { @@ -310,11 +310,11 @@ describe('expressHandler', async () => { }); const gotChunks: any[] = []; - for await (const chunk of result.stream()) { + for await (const chunk of result.stream) { gotChunks.push(chunk); } - const output = await result.output(); + const output = await result.output; assert.strictEqual(output.finishReason, 'stop'); assert.deepStrictEqual(output.message, { role: 'model', @@ -502,7 +502,7 @@ describe('startFlowServer', async () => { }); const gotChunks: GenerateResponseChunkData[] = []; - for await (const chunk of result.stream()) { + for await (const chunk of result.stream) { gotChunks.push(chunk); } @@ -512,7 +512,7 @@ describe('startFlowServer', async () => { { index: 0, role: 'model', content: [{ text: '1' }] }, ]); - assert.strictEqual(await result.output(), 'Echo: olleh'); + assert.strictEqual(await result.output, 'Echo: olleh'); }); }); }); diff --git a/js/plugins/firebase/tests/functions_test.ts b/js/plugins/firebase/tests/functions_test.ts index 7e50635fa..e31f65798 100644 --- a/js/plugins/firebase/tests/functions_test.ts +++ b/js/plugins/firebase/tests/functions_test.ts @@ -166,11 +166,11 @@ describe('function', () => { }); const chunks: any[] = []; - for await (const chunk of result.stream()) { + for await (const chunk of result.stream) { chunks.push(chunk); } - expect(await result.output()).toBe('hi Pavel - {"user":"Ali Baba"}'); + expect(await result.output).toBe('hi Pavel - {"user":"Ali Baba"}'); expect(chunks).toStrictEqual([{ chubk: 1 }, { chubk: 2 }, { chubk: 3 }]); }); }); diff --git a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts index 293bdf106..fc23374f4 100644 --- a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts +++ b/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts @@ -117,7 +117,7 @@ export class ChatbotComponent { }); let textBlock: OutputSchema | undefined = undefined; - for await (const chunk of response.stream()) { + for await (const chunk of response.stream) { for (const content of chunk.content) { if (content.text) { if (!textBlock) { @@ -133,7 +133,7 @@ export class ChatbotComponent { this.loading = false; this.chatFormControl.enable(); - await response.output(); + await response.output; } catch (e) { this.loading = false; this.chatFormControl.enable(); diff --git a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts index 98e454219..1fa9d6900 100644 --- a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts +++ b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts @@ -109,7 +109,7 @@ export class ChatbotComponent { }); let textBlock: OutputSchema | undefined = undefined; - for await (const chunk of response.stream()) { + for await (const chunk of response.stream) { for (const content of chunk.content) { if (content.text) { if (!textBlock) { diff --git a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts index 9d39d3148..c9444ec95 100644 --- a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts +++ b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts @@ -44,10 +44,10 @@ export class StreamingJSONComponent { url, input: parseInt(this.count), }); - for await (const chunk of response.stream()) { + for await (const chunk of response.stream) { this.characters = chunk; } - console.log('streamConsumer done', await response.output()); + console.log('streamConsumer done', await response.output); this.loading = false; } catch (e) { this.loading = false; diff --git a/tests/src/flow_server_test.ts b/tests/src/flow_server_test.ts index 06d83c943..5b301035b 100644 --- a/tests/src/flow_server_test.ts +++ b/tests/src/flow_server_test.ts @@ -68,7 +68,7 @@ async function testFlowServer() { input: test.post.data, }); - for await (const chunk of response.stream()) { + for await (const chunk of response.stream) { expected = want.message.replace('{count}', chunkCount.toString()); let chunkJSON = JSON.stringify(await chunk); if (chunkJSON != expected) { @@ -83,7 +83,7 @@ async function testFlowServer() { `unexpected number of stream chunks received: got ${chunkCount}, want: ${test.post.data}` ); } - let out = await response.output(); + let out = await response.output; want.result = want.result.replace(/\{count\}/g, chunkCount.toString()); if (out != want.result) { throw new Error( From 76aa1df35b852e99e1b0bb5c8d007b5aa297cce1 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Wed, 29 Jan 2025 14:20:07 -0500 Subject: [PATCH 364/562] fix(js/ai/prompt): also delete null description from input schema (#1691) --- js/ai/src/prompt.ts | 5 ++++- js/genkit/tests/prompts_test.ts | 12 +++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/js/ai/src/prompt.ts b/js/ai/src/prompt.ts index bdbe20ba2..901b3685c 100644 --- a/js/ai/src/prompt.ts +++ b/js/ai/src/prompt.ts @@ -739,7 +739,10 @@ function loadPrompt( // dotprompt can set null description on the schema, which can confuse downstream schema consumers if (promptMetadata.output?.schema?.description === null) { - delete promptMetadata.output?.schema?.description; + delete promptMetadata.output.schema.description; + } + if (promptMetadata.input?.schema?.description === null) { + delete promptMetadata.input.schema.description; } return { diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index 816d4e738..f72d0e2aa 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -889,7 +889,7 @@ describe('definePrompt', () => { }); }); -describe.only('prompt', () => { +describe('prompt', () => { let ai: GenkitBeta; let pm: ProgrammableModel; @@ -1031,6 +1031,16 @@ describe.only('prompt', () => { required: ['output'], type: 'object', }); + + assert.deepStrictEqual( + (await (await prompt.asTool())({ foo: 'bar' })).messages, + [ + { + role: 'user', + content: [{ text: 'Write a poem about bar.' }], + }, + ] + ); }); it('loads a varaint from from the folder', async () => { From fd4d3b393cdc3267d9ab5662626b2beeb5b47360 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:20:47 +0000 Subject: [PATCH 365/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.10 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 6a22abad2..772d28ac2 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 1685f62bf27297411848d17070ab0ccfcb1660a8 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:20:49 +0000 Subject: [PATCH 366/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.10 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 555eb52d0..0befb6cd9 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From d91483b22a0e2b63b64fd610d2b7d91a36883176 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:20:51 +0000 Subject: [PATCH 367/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.10 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index 0f7ea6caf..f35df5713 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From f7de549fc504c95aa586cd88533b932b4b89909b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:07 +0000 Subject: [PATCH 368/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.10 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 67521cc47..605c9335e 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From c7029b505736072016745fa97cf38834c64bb993 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:11 +0000 Subject: [PATCH 369/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.10 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index 8e454c5a0..784cf78dc 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From 5ef266206f3411e782b2c074889362d3d15384a4 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:14 +0000 Subject: [PATCH 370/562] chore: bump genkit version to genkit@1.0.0-rc.10 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index 965050290..c2d999cfb 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From 960d7e1175787fd65f15439c33bf6a8bb7fa7138 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:22 +0000 Subject: [PATCH 371/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.10 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index 37e33d301..8954ea23a 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From 79472e177667332f309515958277aedd06c99ece Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:25 +0000 Subject: [PATCH 372/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.10 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index ef0b37b8f..c3402591b 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From 8f9ee4a04ae5e68d5712b58acb0aba35545c35a5 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:28 +0000 Subject: [PATCH 373/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.10 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 02ab8efa4..d6a611bff 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From 3efa16bb026137d64d3091ad94931da148a93619 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:33 +0000 Subject: [PATCH 374/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.10 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 03646e134..13f06f3fe 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From 6f484095e4a81c1b2449c905d4afc0ea2bbaaad1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:35 +0000 Subject: [PATCH 375/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.10 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index 6cc023183..b772a7254 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From 74a2d8f27fd50e5972eb30469eac84fe0f8d40ac Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:38 +0000 Subject: [PATCH 376/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.10 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 8dc50bfa5..e06e849b6 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From f76ea27e41253cad0347c3da48642025e427e586 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:41 +0000 Subject: [PATCH 377/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.10 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index 5ac2bc2b2..77b2a4b91 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From 43f24a4b27fe41d7996f08cf2900d08a4fe732a6 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:44 +0000 Subject: [PATCH 378/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.10 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index 617c5b442..f556160b9 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From 4abfd69fd8355ad96ce0001761dbacd9ddc80e1a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:47 +0000 Subject: [PATCH 379/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.10 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index b252d87b2..0faeffeee 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From 373e3b8214526e70aacc08d00580dedce6ac0ba5 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:50 +0000 Subject: [PATCH 380/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.10 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 14169f369..412d4055b 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From 390b91bf46227bc2f156ab3ec6cd78bc161fbd5d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:54 +0000 Subject: [PATCH 381/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.10 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index 36d28b399..a0d999b8c 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From a9b3dc6867c42a4a4298cccaacd9fff6981dbe43 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:22:57 +0000 Subject: [PATCH 382/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.10 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index 00628c507..2df8d98dc 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From c12dd0943e842563edfbb4cc3eafdfc23c907eb3 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Wed, 29 Jan 2025 19:23:00 +0000 Subject: [PATCH 383/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.10 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index 9cb879a4d..6792e0a61 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.9", + "version": "1.0.0-rc.10", "type": "commonjs", "scripts": { "check": "tsc", From b9ee88e7658f8909edfed15be5eb9b2769c4c4bd Mon Sep 17 00:00:00 2001 From: thedmail Date: Wed, 29 Jan 2025 11:54:22 -0800 Subject: [PATCH 384/562] docs: Fix out-of-sequence instructions (#1684) docs: Pre-PR version sends you to install CLI, which tells you to create a project. Then, when you come back, you get told to create a project again (with additional requirements). This PR improves the UX by fixing this issue. --- docs/firebase.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/firebase.md b/docs/firebase.md index 9c67a1098..4f81324b0 100644 --- a/docs/firebase.md +++ b/docs/firebase.md @@ -6,7 +6,6 @@ as callable functions using the Cloud Functions client libraries. ## Before you begin -* Install the [Firebase CLI](/docs/cli). * You should be familiar with Genkit's concept of [flows](flows), and how to write them. The instructions on this page assume that you already have some flows defined, which you want to deploy. @@ -24,6 +23,8 @@ up, follow these steps: 1. Upgrade the project to the Blaze plan, which is required to deploy Cloud Functions. +1. Install the [Firebase CLI](/docs/cli). + 1. Log in with the Firebase CLI: ```posix-terminal From e5979b920272c0803a79760a9c60727f326be624 Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Wed, 29 Jan 2025 16:33:15 -0500 Subject: [PATCH 385/562] fix: Add back in dotprompt plugin folder (#1694) --- js/plugins/dotprompt/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 js/plugins/dotprompt/.gitkeep diff --git a/js/plugins/dotprompt/.gitkeep b/js/plugins/dotprompt/.gitkeep new file mode 100644 index 000000000..e69de29bb From 2f0bd0e880b1050d6eff0f097dd3c15bbfe23be0 Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Wed, 29 Jan 2025 16:33:48 -0500 Subject: [PATCH 386/562] fix: Remove dotprompt folder (#1695) * fix: Remove dotprompt folder From a5beeb069c744e2f9375870cfdbc49e63173e1a9 Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Wed, 29 Jan 2025 16:43:01 -0500 Subject: [PATCH 387/562] Remove gitkeep folder (#1697) --- js/plugins/dotprompt/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 js/plugins/dotprompt/.gitkeep diff --git a/js/plugins/dotprompt/.gitkeep b/js/plugins/dotprompt/.gitkeep deleted file mode 100644 index e69de29bb..000000000 From 9a91b043d2e26b1b0f227fa3e5ed6ea789a51158 Mon Sep 17 00:00:00 2001 From: ifielker Date: Wed, 29 Jan 2025 17:45:59 -0500 Subject: [PATCH 388/562] feat(multimodal)!: Added Multimodal support for Vertex AI (#1671) * feat(multimodal)!: Added Multimodal support for Vertex AI BREAKING CHANGE: Embedding type now includes metadata and ai.embed returns an array of Embedding instead of a single embedding per document. --- docs/plugins/vertex-ai.md | 22 +- js/ai/src/document.ts | 133 +++++++- js/ai/src/embedder.ts | 21 +- js/ai/tests/model/document_test.ts | 286 +++++++++++++++++- js/genkit/src/genkit.ts | 2 +- js/genkit/tests/embed_test.ts | 18 +- js/plugins/chroma/src/index.ts | 68 +++-- js/plugins/dev-local-vectorstore/package.json | 2 +- js/plugins/dev-local-vectorstore/src/index.ts | 27 +- .../src/metrics/answer_relevancy.ts | 4 +- js/plugins/firebase/src/firestoreRetriever.ts | 4 +- js/plugins/googleai/src/embedder.ts | 4 +- js/plugins/ollama/src/embeddings.ts | 4 +- js/plugins/ollama/tests/embeddings_test.ts | 2 +- js/plugins/pinecone/src/index.ts | 57 ++-- js/plugins/vertexai/src/embedder.ts | 285 +++++++++++++++-- js/plugins/vertexai/src/gemini.ts | 4 +- js/plugins/vertexai/src/index.ts | 2 + js/plugins/vertexai/src/predict.ts | 7 +- .../vectorsearch/vector_search/retrievers.ts | 2 +- js/pnpm-lock.yaml | 109 +++++++ js/testapps/multimodal/docs/BirthdayPets.pdf | Bin 0 -> 254347 bytes js/testapps/multimodal/package.json | 44 +++ js/testapps/multimodal/src/genkit.ts | 38 +++ js/testapps/multimodal/src/index.ts | 18 ++ js/testapps/multimodal/src/pdf.ts | 182 +++++++++++ js/testapps/multimodal/src/prompt.ts | 91 ++++++ js/testapps/multimodal/src/video.ts | 173 +++++++++++ js/testapps/multimodal/tsconfig.json | 16 + js/testapps/ollama/src/index.ts | 20 +- js/testapps/rag/src/pdf-rag-firebase.ts | 10 +- 31 files changed, 1514 insertions(+), 141 deletions(-) create mode 100644 js/testapps/multimodal/docs/BirthdayPets.pdf create mode 100644 js/testapps/multimodal/package.json create mode 100644 js/testapps/multimodal/src/genkit.ts create mode 100644 js/testapps/multimodal/src/index.ts create mode 100644 js/testapps/multimodal/src/pdf.ts create mode 100644 js/testapps/multimodal/src/prompt.ts create mode 100644 js/testapps/multimodal/src/video.ts create mode 100644 js/testapps/multimodal/tsconfig.json diff --git a/docs/plugins/vertex-ai.md b/docs/plugins/vertex-ai.md index 511ba521a..fb06f5c89 100644 --- a/docs/plugins/vertex-ai.md +++ b/docs/plugins/vertex-ai.md @@ -143,6 +143,26 @@ const embedding = await ai.embed({ }); ``` +This plugin can also handle multimodal embeddings: + +```ts +const ai = genkit({ + plugins: [vertextAI({location: 'us-central1' })], +}); + +const embeddings = await ai.embed({ + embedder: multimodalEmbedding001, + content: { + content: [{ + "media": { + "url": "gs://cloud-samples-data/generative-ai/video/pixel8.mp4", + "contentType": "video/mp4" + } + }] + } +}); +``` + Imagen3 model allows generating images from user prompt: ```ts @@ -573,6 +593,6 @@ const llmResponse = await ai.generate({ Only specific models, such as `gemini15Flash` and `gemini15Pro`, support context caching, and currently only on version numbers `001`. If an unsupported model is used, an error will be raised, indicating that caching cannot be applied. -### Further Reading +### Further Reading See more information regarding context caching on Vertex AI in their [documentation](https://cloud.google.com/vertex-ai/generative-ai/docs/context-cache/context-cache-overview). diff --git a/js/ai/src/document.ts b/js/ai/src/document.ts index 70ed10f17..18e7ee269 100644 --- a/js/ai/src/document.ts +++ b/js/ai/src/document.ts @@ -15,6 +15,7 @@ */ import { z } from '@genkit-ai/core'; +import { Embedding } from './embedder'; const EmptyPartSchema = z.object({ text: z.never().optional(), @@ -40,12 +41,21 @@ export type MediaPart = z.infer; export const PartSchema = z.union([TextPartSchema, MediaPartSchema]); export type Part = z.infer; +// We need both metadata and embedMetadata because they can +// contain the same fields (e.g. video start/stop) with different values. export const DocumentDataSchema = z.object({ content: z.array(PartSchema), metadata: z.record(z.string(), z.any()).optional(), }); export type DocumentData = z.infer; +function deepCopy(value: T): T { + if (value === undefined) { + return value; + } + return JSON.parse(JSON.stringify(value)) as T; +} + /** * Document represents document content along with its metadata that can be embedded, indexed or * retrieved. Each document can contain multiple parts (for example text and an image) @@ -55,8 +65,8 @@ export class Document implements DocumentData { metadata?: Record; constructor(data: DocumentData) { - this.content = data.content; - this.metadata = data.metadata; + this.content = deepCopy(data.content); + this.metadata = deepCopy(data.metadata); } static fromText(text: string, metadata?: Record) { @@ -66,6 +76,37 @@ export class Document implements DocumentData { }); } + // Construct a Document from a single media item + static fromMedia( + url: string, + contentType?: string, + metadata?: Record + ) { + return new Document({ + content: [ + { + media: { + contentType, + url, + }, + }, + ], + metadata, + }); + } + + // Construct a Document from content + static fromData( + data: string, + dataType?: string, + metadata?: Record + ) { + if (dataType === 'text') { + return this.fromText(data, metadata); + } + return this.fromMedia(data, dataType, metadata); + } + /** * Concatenates all `text` parts present in the document with no delimiter. * @returns A string of all concatenated text parts. @@ -75,18 +116,90 @@ export class Document implements DocumentData { } /** - * Returns the first media part detected in the document. Useful for extracting - * (for example) an image. - * @returns The first detected `media` part in the document. + * Media array getter. + * @returns the array of media parts. */ - get media(): { url: string; contentType?: string } | null { - return this.content.find((part) => part.media)?.media || null; + get media(): { url: string; contentType?: string }[] { + return this.content + .filter((part) => part.media && !part.text) + .map((part) => part.media!); + } + + /** + * Gets the first item in the document. Either text or media url. + */ + get data(): string { + // + if (this.text) { + return this.text; + } + if (this.media) { + return this.media[0].url; + } + return ''; + } + + /** + * Gets the contentType of the data that is returned by data() + */ + get dataType(): string | undefined { + if (this.text) { + return 'text'; + } + if (this.media && this.media[0].contentType) { + return this.media[0].contentType; + } + return undefined; } toJSON(): DocumentData { return { - content: this.content, - metadata: this.metadata, - }; + content: deepCopy(this.content), + metadata: deepCopy(this.metadata), + } as DocumentData; + } + + /** + * Embedders may return multiple embeddings for a single document. + * But storage still requires a 1:1 relationship. So we create an + * array of Documents from a single document - one per embedding. + * @param embeddings The embeddings to create the documents from. + * @returns an array of documents based on this document and the embeddings. + */ + getEmbeddingDocuments(embeddings: Embedding[]): Document[] { + let documents: Document[] = []; + for (const embedding of embeddings) { + let jsonDoc = this.toJSON(); + if (embedding.metadata) { + if (!jsonDoc.metadata) { + jsonDoc.metadata = {}; + } + jsonDoc.metadata.embedMetadata = embedding.metadata; + } + documents.push(new Document(jsonDoc)); + } + checkUniqueDocuments(documents); + return documents; + } +} + +// Unique documents are important because we key +// our vector storage on the Md5 hash of the JSON.stringify(document) +// So if we have multiple duplicate documents with +// different embeddings, we will either skip or overwrite +// those entries and lose embedding information. +// Export and boolean return value for testing only. +export function checkUniqueDocuments(documents: Document[]): boolean { + const seen = new Set(); + for (const doc of documents) { + const serialized = JSON.stringify(doc); + if (seen.has(serialized)) { + console.warn( + 'Warning: embedding documents are not unique. Are you missing embed metadata?' + ); + return false; + } + seen.add(serialized); } + return true; } diff --git a/js/ai/src/embedder.ts b/js/ai/src/embedder.ts index 0f95adb7b..5bafe329b 100644 --- a/js/ai/src/embedder.ts +++ b/js/ai/src/embedder.ts @@ -19,14 +19,19 @@ import { Registry } from '@genkit-ai/core/registry'; import { Document, DocumentData, DocumentDataSchema } from './document.js'; /** - * Embedding vector. + * A batch (array) of embeddings. */ -export type Embedding = number[]; +export type EmbeddingBatch = { embedding: number[] }[]; /** - * A batch (array) of embeddings. + * EmbeddingSchema includes the embedding and also metadata so you know + * which of multiple embeddings corresponds to which part of a document. */ -export type EmbeddingBatch = { embedding: Embedding }[]; +export const EmbeddingSchema = z.object({ + embedding: z.array(z.number()), + metadata: z.record(z.string(), z.unknown()).optional(), +}); +export type Embedding = z.infer; /** * A function used for embedder definition, encapsulates embedder implementation. @@ -48,7 +53,7 @@ const EmbedRequestSchema = z.object({ * Zod schema of an embed response. */ const EmbedResponseSchema = z.object({ - embeddings: z.array(z.object({ embedding: z.array(z.number()) })), + embeddings: z.array(EmbeddingSchema), // TODO: stats, etc. }); type EmbedResponse = z.infer; @@ -138,7 +143,7 @@ export type EmbedderArgument< export async function embed( registry: Registry, params: EmbedderParams -): Promise { +): Promise { let embedder = await resolveEmbedder(registry, params); if (!embedder.embedderAction) { let embedderId: string; @@ -162,7 +167,7 @@ export async function embed( ...params.options, }, }); - return response.embeddings[0].embedding; + return response.embeddings; } interface ResolvedEmbedder { @@ -248,7 +253,7 @@ export const EmbedderInfoSchema = z.object({ supports: z .object({ /** Model can input this type of data. */ - input: z.array(z.enum(['text', 'image'])).optional(), + input: z.array(z.enum(['text', 'image', 'video'])).optional(), /** Model can support multiple languages */ multilingual: z.boolean().optional(), }) diff --git a/js/ai/tests/model/document_test.ts b/js/ai/tests/model/document_test.ts index b8f4e30c1..5ef73c828 100644 --- a/js/ai/tests/model/document_test.ts +++ b/js/ai/tests/model/document_test.ts @@ -16,17 +16,49 @@ import * as assert from 'assert'; import { describe, it } from 'node:test'; -import { Document } from '../../src/document.js'; +import { Document, checkUniqueDocuments } from '../../src/document.js'; +import { Embedding } from '../../src/embedder.js'; describe('document', () => { + describe('constructor', () => { + it('makes a copy, not a reference', () => { + let content = [ + { media: { url: 'data:foo' } }, + { media: { url: 'data:bar' } }, + ]; + let metadata = { bar: 'baz', embedMetadata: { bar: 'qux' } }; + const doc = new Document({ content, metadata }); + + // Change the deep parts of the content + if (doc.content[0].media) { + content[0].media.url = 'data: bam'; + assert.equal(doc.content[0].media.url, 'data:foo'); + } else { + assert.fail('doc.content[0].media is not present'); + } + + // Change the deep parts of the metadata + if ( + doc.metadata && + doc.metadata.embedMetadata && + doc.metadata.embedMetadata.bar + ) { + metadata.embedMetadata.bar = 'boom'; + assert.equal(doc.metadata.embedMetadata.bar, 'qux'); + } else { + assert.fail('doc.metadata.embedMetadata.bar is not present'); + } + }); + }); + describe('text()', () => { - it('retuns single text part', () => { + it('returns single text part', () => { const doc = new Document({ content: [{ text: 'foo' }] }); assert.equal(doc.text, 'foo'); }); - it('retuns concatenated text part', () => { + it('returns concatenated text part', () => { const doc = new Document({ content: [{ text: 'foo' }, { text: 'bar' }] }); assert.equal(doc.text, 'foobar'); @@ -34,7 +66,7 @@ describe('document', () => { }); describe('media()', () => { - it('retuns first media part', () => { + it('returns an array of media', () => { const doc = new Document({ content: [ { media: { url: 'data:foo' } }, @@ -42,26 +74,103 @@ describe('document', () => { ], }); - assert.deepEqual(doc.media, { url: 'data:foo' }); + assert.deepEqual(doc.media, [{ url: 'data:foo' }, { url: 'data:bar' }]); + }); + }); + + describe('data()', () => { + it('returns the text in a text document', () => { + const doc = Document.fromText('foo'); + assert.equal(doc.data, 'foo'); + }); + + it('returns the image in an image document', () => { + const url = 'gs://somebucket/someimage.png'; + const doc = Document.fromMedia(url, 'image/png'); + assert.equal(doc.data, url); + }); + + it('returns the video in a video document', () => { + const url = 'gs://somebucket/somevideo.mp4'; + const doc = Document.fromMedia(url, 'video/mp4'); + assert.equal(doc.data, url); + }); + }); + + describe('dataType()', () => { + it('returns "text" for a text document', () => { + const doc = Document.fromText('foo'); + assert.equal(doc.dataType, 'text'); + }); + + it('returns the image type in an image document', () => { + const contentType = 'image/png'; + const doc = Document.fromMedia( + 'gs://somebucket/someimage.png', + contentType + ); + assert.equal(doc.dataType, contentType); + }); + + it('returns the video type in a video document', () => { + const contentType = 'video/mp4'; + const doc = Document.fromMedia( + 'gs://somebucket/somevideo.mp4', + contentType + ); + assert.equal(doc.dataType, contentType); }); }); describe('toJSON()', () => { - it('retuns data object', () => { + it('returns data object', () => { const doc = new Document({ content: [{ text: 'foo' }], - metadata: { bar: 'baz' }, + metadata: { bar: 'baz', embedMetadata: { bar: 'qux' } }, }); assert.deepEqual(doc.toJSON(), { content: [{ text: 'foo' }], - metadata: { bar: 'baz' }, + metadata: { bar: 'baz', embedMetadata: { bar: 'qux' } }, }); }); + + it('makes a copy not a reference', () => { + let content = [ + { media: { url: 'data:foo' } }, + { media: { url: 'data:bar' } }, + ]; + let metadata = { bar: 'baz', embedMetadata: { bar: 'qux' } }; + const doc = new Document({ content, metadata }); + + const jsonDoc = doc.toJSON(); + assert.deepStrictEqual(jsonDoc.content, content); + assert.deepStrictEqual(jsonDoc.metadata, metadata); + + // Change the deep parts of the content in the doc + if (doc.content[0].media) { + doc.content[0].media.url = 'data: bam'; + assert.equal(jsonDoc.content[0].media.url, 'data:foo'); + } else { + assert.fail('doc.content[0].media is not present'); + } + + // Change the deep parts of the metadata in the doc + if ( + doc.metadata && + doc.metadata.embedMetadata && + doc.metadata.embedMetadata.bar + ) { + doc.metadata.embedMetadata.bar = 'boom'; + assert.equal(jsonDoc.metadata.embedMetadata.bar, 'qux'); + } else { + assert.fail('doc.metadata.embedMetadata.bar is not present'); + } + }); }); describe('fromText()', () => { - it('retuns data object', () => { + it('returns Document with text', () => { const doc = Document.fromText('foo', { bar: 'baz' }); assert.deepEqual(doc.toJSON(), { @@ -70,4 +179,163 @@ describe('document', () => { }); }); }); + + describe('fromMedia', () => { + it('returns Document with image and metadata', () => { + const url = 'gs://somebucket/someimage.jpg'; + const contentType = 'image/jpeg'; + const metadata = { embedMetadata: { embeddingType: 'image' } }; + const doc = Document.fromMedia(url, contentType, metadata); + assert.deepEqual(doc.toJSON(), { + content: [ + { + media: { + contentType, + url, + }, + }, + ], + metadata, + }); + }); + + it('returns Document with video and metadata', () => { + const url = 'gs://somebucket/somevideo.mp4'; + const contentType = 'video/mp4'; + const metadata = { + start: 0, + end: 120, + embedMetadata: { embeddingType: 'video', start: 15, end: 30 }, + }; + const doc = Document.fromMedia(url, contentType, metadata); + assert.deepEqual(doc.toJSON(), { + content: [ + { + media: { + contentType, + url, + }, + }, + ], + metadata, + }); + }); + }); + + describe('fromData', () => { + it('returns a Document with text', () => { + const data = 'foo'; + const dataType = 'text'; + const metadata = { embedMetadata: { embeddingType: 'text' } }; + const doc = Document.fromData(data, dataType, metadata); + assert.deepEqual(doc.toJSON(), { + content: [{ text: data }], + metadata, + }); + }); + + it('returns a Document with image', () => { + const data = 'iVBORw0KGgoAAAANSUhEUgAAAAjCB0C8AAAAASUVORK5CYII='; + const dataType = 'image/png'; + const metadata = { embedMetadata: { embeddingType: 'image' } }; + const doc = Document.fromData(data, dataType, metadata); + assert.deepEqual(doc.toJSON(), { + content: [ + { + media: { + contentType: dataType, + url: data, + }, + }, + ], + metadata, + }); + }); + + it('returns a Document with video', () => { + const data = 'gs://somebucket/somevideo.mp4'; + const dataType = 'video/mp4'; + const metadata = { + start: 0, + end: 120, + embedMetadata: { embeddingType: 'video', start: 15, end: 30 }, + }; + const doc = Document.fromData(data, dataType, metadata); + assert.deepEqual(doc.toJSON(), { + content: [ + { + media: { + contentType: dataType, + url: data, + }, + }, + ], + metadata, + }); + }); + }); + + describe('getEmbeddingDocuments', () => { + it('returns the same document for single embedding', () => { + const doc = Document.fromText('foo'); + const embeddings: Embedding[] = [ + { + embedding: [0.1, 0.2, 0.3], + }, + ]; + const docs = doc.getEmbeddingDocuments(embeddings); + assert.deepEqual(docs, [doc]); + }); + + it('returns an array of document for multiple embeddings', () => { + const url = 'gs://somebucket/somevideo.mp4'; + const contentType = 'video/mp4'; + const metadata = { start: 0, end: 60 }; + const doc = Document.fromMedia(url, contentType, metadata); + + let embeddings: Embedding[] = []; + for (var start = 0; start < 60; start += 15) { + embeddings.push(makeTestEmbedding(start)); + } + const docs = doc.getEmbeddingDocuments(embeddings); + assert.equal(docs.length, embeddings.length); + for (var i = 0; i < docs.length; i++) { + assert.deepEqual(docs[i].toJSON().content, doc.toJSON().content); + assert.deepEqual( + docs[i].toJSON().metadata?.embedMetadata, + embeddings[i].metadata + ); + var origMetadata = JSON.parse( + JSON.stringify(docs[i].metadata) + ) as Record; + delete origMetadata.embedMetadata; + assert.deepEqual(origMetadata, doc.toJSON().metadata); + } + }); + + it('returns unique embedding documents', () => { + const url = 'gs://somebucket/somevideo.mp4'; + const contentType = 'video/mp4'; + const metadata = { start: 0, end: 60 }; + const doc = Document.fromMedia(url, contentType, metadata); + + let embeddings: Embedding[] = []; + for (var start = 0; start < 60; start += 15) { + embeddings.push(makeTestEmbedding(start)); + } + const docs = doc.getEmbeddingDocuments(embeddings); + assert.equal(checkUniqueDocuments(docs), true); + }); + }); + + function makeTestEmbedding(start: number): Embedding { + return { + embedding: [0.1, 0.2, 0.3], + metadata: { + embeddingType: 'video', + start: start, + end: start + 15, + }, + }; + } }); diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index e27de15e8..e780a04ea 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -499,7 +499,7 @@ export class Genkit implements HasRegistry { */ embed( params: EmbedderParams - ): Promise { + ): Promise { return embed(this.registry, params); } diff --git a/js/genkit/tests/embed_test.ts b/js/genkit/tests/embed_test.ts index c93c6ca51..8483cc2e1 100644 --- a/js/genkit/tests/embed_test.ts +++ b/js/genkit/tests/embed_test.ts @@ -30,10 +30,12 @@ describe('embed', () => { }); it('passes string content as docs', async () => { - const response = await ai.embed({ - embedder: 'echoEmbedder', - content: 'hi', - }); + const response = ( + await ai.embed({ + embedder: 'echoEmbedder', + content: 'hi', + }) + )[0].embedding; assert.deepStrictEqual((embedder as any).lastRequest, [ [Document.fromText('hi')], { @@ -54,7 +56,7 @@ describe('embed', () => { version: undefined, }, ]); - assert.deepStrictEqual(response, [1, 2, 3, 4]); + assert.deepStrictEqual(response, [{ embedding: [1, 2, 3, 4] }]); }); }); @@ -75,7 +77,7 @@ describe('embed', () => { temperature: 11, }, }); - assert.deepStrictEqual(response, [1, 2, 3, 4]); + assert.deepStrictEqual(response, [{ embedding: [1, 2, 3, 4] }]); assert.deepStrictEqual((embedder as any).lastRequest[1], { temperature: 11, version: undefined, @@ -95,7 +97,7 @@ describe('embed', () => { temperature: 11, }, }); - assert.deepStrictEqual(response, [1, 2, 3, 4]); + assert.deepStrictEqual(response, [{ embedding: [1, 2, 3, 4] }]); assert.deepStrictEqual((embedder as any).lastRequest[1], { temperature: 11, version: 'abc', @@ -113,7 +115,7 @@ describe('embed', () => { temperature: 11, }, }); - assert.deepStrictEqual(response, [1, 2, 3, 4]); + assert.deepStrictEqual(response, [{ embedding: [1, 2, 3, 4] }]); assert.deepStrictEqual((embedder as any).lastRequest[1], { temperature: 11, version: 'abc', diff --git a/js/plugins/chroma/src/index.ts b/js/plugins/chroma/src/index.ts index 5e4727742..1187aaa95 100644 --- a/js/plugins/chroma/src/index.ts +++ b/js/plugins/chroma/src/index.ts @@ -29,6 +29,7 @@ import { import { Document, EmbedderArgument, + Embedding, Genkit, indexerRef, retrieverRef, @@ -151,7 +152,7 @@ export function chromaRetriever( include: getIncludes(options?.include), where: options?.where, whereDocument: options?.whereDocument, - queryEmbeddings: embedding, + queryEmbeddings: embedding[0].embedding, }); const documents = results.documents[0]; @@ -174,9 +175,12 @@ export function chromaRetriever( ); return { - documents: combined.map((result) => - Document.fromText(result.document, result.metadata).toJSON() - ), + documents: combined.map((result) => { + const data = result.document; + const dataType = result.metadata.datatype; + const docMetadata = JSON.parse(result.metadata.docMetadata); + return Document.fromData(data, dataType, docMetadata).toJSON(); + }), }; } ); @@ -207,8 +211,8 @@ function constructMetadata( metadatas: (Metadata | null)[][], embeddings: Embeddings[] | null, distances: number[][] | null -): any { - var fullMetadata: Record = {}; +): unknown { + var fullMetadata: Record = {}; if (metadatas && metadatas[i]) { fullMetadata.metadata = metadatas[i]; } @@ -268,19 +272,32 @@ export function chromaIndexer( ) ); - const entries = embeddings.map((value, i) => { - const metadata: Metadata = { - ...docs[i].metadata, - }; + const entries = embeddings + .map((value, i) => { + const doc = docs[i]; + // The array of embeddings for this document + const docEmbeddings: Embedding[] = value; + const embeddingDocs = doc.getEmbeddingDocuments(docEmbeddings); + return docEmbeddings.map((docEmbedding, j) => { + const metadata: Metadata = { + docMetdata: JSON.stringify(embeddingDocs[j].metadata), + dataType: embeddingDocs[j].dataType || '', + }; + + const data = embeddingDocs[j].data; + const id = Md5.hashStr(JSON.stringify(embeddingDocs[j])); + return { + id, + value: docEmbedding.embedding, + document: data, + metadata, + }; + }); + }) + .reduce((acc, val) => { + return acc.concat(val); + }, []); - const id = Md5.hashStr(JSON.stringify(docs[i])); - return { - id, - value, - document: docs[i].text, - metadata, - }; - }); await collection.add({ ids: entries.map((e) => e.id), embeddings: entries.map((e) => e.value), @@ -293,6 +310,8 @@ export function chromaIndexer( /** * Helper function for creating Chroma collections. + * Currently only available for text + * https://docs.trychroma.com/docs/embeddings/multimodal */ export async function createChromaCollection< EmbedderCustomOptions extends z.ZodTypeAny, @@ -312,12 +331,13 @@ export async function createChromaCollection< chromaEmbedder = { generate(texts: string[]) { return Promise.all( - texts.map((text) => - ai.embed({ - embedder, - content: text, - options: params.embedderOptions, - }) + texts.map( + (text) => + ai.embed({ + embedder, + content: text, + options: params.embedderOptions, + })[0].embedding // Text only has a single embedding ) ); }, diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index c3402591b..a8cf199cf 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -44,9 +44,9 @@ "types": "./lib/index.d.ts", "exports": { ".": { + "types": "./lib/index.d.ts", "require": "./lib/index.js", "import": "./lib/index.mjs", - "types": "./lib/index.d.ts", "default": "./lib/index.js" } } diff --git a/js/plugins/dev-local-vectorstore/src/index.ts b/js/plugins/dev-local-vectorstore/src/index.ts index 1331c09e8..e85181165 100644 --- a/js/plugins/dev-local-vectorstore/src/index.ts +++ b/js/plugins/dev-local-vectorstore/src/index.ts @@ -16,7 +16,7 @@ import similarity from 'compute-cosine-similarity'; import * as fs from 'fs'; -import { Genkit, z } from 'genkit'; +import { Embedding, Genkit, z } from 'genkit'; import { EmbedderArgument } from 'genkit/embedder'; import { GenkitPlugin, genkitPlugin } from 'genkit/plugin'; import { @@ -32,7 +32,7 @@ const _LOCAL_FILESTORE = '__db_{INDEX_NAME}.json'; interface DbValue { doc: DocumentData; - embedding: Array; + embedding: Embedding; } function loadFilestore(indexName: string) { @@ -45,13 +45,12 @@ function loadFilestore(indexName: string) { } function addDocument( + embedding: Embedding, doc: Document, - contents: Record, - embedding: Array + contents: Record ) { const id = Md5.hashStr(JSON.stringify(doc)); if (!(id in contents)) { - // Only inlcude if doc is new contents[id] = { doc, embedding }; } else { console.debug(`Skipping ${id} since it is already present`); @@ -122,19 +121,22 @@ async function importDocumentsToLocalVectorstore< await Promise.all( docs.map(async (doc) => { - const embedding = await ai.embed({ + const embeddings = await ai.embed({ embedder, content: doc, options: embedderOptions, }); - addDocument(doc, data, embedding); + const embeddingDocs = doc.getEmbeddingDocuments(embeddings); + for (const i in embeddingDocs) { + addDocument(embeddings[i], embeddingDocs[i], data); + } }) ); // Update the file fs.writeFileSync( _LOCAL_FILESTORE.replace('{INDEX_NAME}', params.indexName), - JSON.stringify(data) + JSON.stringify(data, null, 2) ); } @@ -148,8 +150,8 @@ async function getClosestDocuments< }): Promise { const scoredDocs: { score: number; doc: Document }[] = []; // Very dumb way to check for similar docs. - for (const [, value] of Object.entries(params.db)) { - const thisEmbedding = value.embedding; + for (const value of Object.values(params.db)) { + const thisEmbedding = value.embedding.embedding; const score = similarity(params.queryEmbeddings, thisEmbedding) ?? 0; scoredDocs.push({ score, @@ -180,8 +182,7 @@ function configureDevLocalRetriever( }, async (content, options) => { const db = loadFilestore(params.indexName); - - const embedding = await ai.embed({ + const embeddings = await ai.embed({ embedder, content, options: embedderOptions, @@ -189,7 +190,7 @@ function configureDevLocalRetriever( return { documents: await getClosestDocuments({ k: options?.k ?? 3, - queryEmbeddings: embedding, + queryEmbeddings: embeddings[0].embedding, db, }), }; diff --git a/js/plugins/evaluators/src/metrics/answer_relevancy.ts b/js/plugins/evaluators/src/metrics/answer_relevancy.ts index 945ee5b0c..13c669eba 100644 --- a/js/plugins/evaluators/src/metrics/answer_relevancy.ts +++ b/js/plugins/evaluators/src/metrics/answer_relevancy.ts @@ -82,12 +82,12 @@ export async function answerRelevancyScore< embedder, content: input, options: embedderOptions, - }); + })[0].embedding; // Single embedding for text const genQuestionEmbed = await ai.embed({ embedder, content: genQuestion, options: embedderOptions, - }); + })[0].embedding; // Single embedding for text const score = cosineSimilarity(questionEmbed, genQuestionEmbed); const answered = response.output?.answered === '1' ? 1 : 0; const isNonCommittal = response.output?.noncommittal === '1' ? 1 : 0; diff --git a/js/plugins/firebase/src/firestoreRetriever.ts b/js/plugins/firebase/src/firestoreRetriever.ts index c0a6e0f35..97739a859 100644 --- a/js/plugins/firebase/src/firestoreRetriever.ts +++ b/js/plugins/firebase/src/firestoreRetriever.ts @@ -123,7 +123,9 @@ export function defineFirestoreRetriever( }), }, async (input, options) => { - const embedding = await ai.embed({ embedder, content: input }); + // Single embedding for text input + const embedding = await ai.embed({ embedder, content: input })[0] + .embedding; if (!options.collection && !collection) { throw new Error( 'Must specify a collection to query in Firestore retriever.' diff --git a/js/plugins/googleai/src/embedder.ts b/js/plugins/googleai/src/embedder.ts index 31e27afa0..756d0a8b5 100644 --- a/js/plugins/googleai/src/embedder.ts +++ b/js/plugins/googleai/src/embedder.ts @@ -15,7 +15,7 @@ */ import { EmbedContentRequest, GoogleGenerativeAI } from '@google/generative-ai'; -import { EmbedderReference, Genkit, z } from 'genkit'; +import { EmbedderAction, EmbedderReference, Genkit, z } from 'genkit'; import { embedderRef } from 'genkit/embedder'; import { PluginOptions } from './index.js'; @@ -81,7 +81,7 @@ export function defineGoogleAIEmbedder( ai: Genkit, name: string, options: PluginOptions -) { +): EmbedderAction { let apiKey = options?.apiKey || process.env.GOOGLE_GENAI_API_KEY || diff --git a/js/plugins/ollama/src/embeddings.ts b/js/plugins/ollama/src/embeddings.ts index 6d4dc771a..49bede959 100644 --- a/js/plugins/ollama/src/embeddings.ts +++ b/js/plugins/ollama/src/embeddings.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Document, Genkit } from 'genkit'; +import { Document, EmbedderAction, Genkit } from 'genkit'; import { EmbedRequest, EmbedResponse } from 'ollama'; import { DefineOllamaEmbeddingParams, RequestHeaders } from './types.js'; @@ -62,7 +62,7 @@ async function toOllamaEmbedRequest( export function defineOllamaEmbedder( ai: Genkit, { name, modelName, dimensions, options }: DefineOllamaEmbeddingParams -) { +): EmbedderAction { return ai.defineEmbedder( { name: `ollama/${name}`, diff --git a/js/plugins/ollama/tests/embeddings_test.ts b/js/plugins/ollama/tests/embeddings_test.ts index d2a20cb44..eac091c5b 100644 --- a/js/plugins/ollama/tests/embeddings_test.ts +++ b/js/plugins/ollama/tests/embeddings_test.ts @@ -69,7 +69,7 @@ describe('defineOllamaEmbedder', () => { embedder, content: 'Hello, world!', }); - assert.deepStrictEqual(result, [0.1, 0.2, 0.3]); + assert.deepStrictEqual(result, [{ embedding: [0.1, 0.2, 0.3] }]); }); it('should handle API errors correctly', async () => { diff --git a/js/plugins/pinecone/src/index.ts b/js/plugins/pinecone/src/index.ts index 54268430b..f3fb65c97 100644 --- a/js/plugins/pinecone/src/index.ts +++ b/js/plugins/pinecone/src/index.ts @@ -23,7 +23,7 @@ import { import { Genkit, z } from 'genkit'; import { GenkitPlugin, genkitPlugin } from 'genkit/plugin'; -import { EmbedderArgument } from 'genkit/embedder'; +import { EmbedderArgument, Embedding } from 'genkit/embedder'; import { CommonRetrieverOptionsSchema, Document, @@ -60,6 +60,7 @@ const PineconeIndexerOptionsSchema = z.object({ }); const CONTENT_KEY = '_content'; +const CONTENT_TYPE = '_contentType'; /** * pineconeRetrieverRef function creates a retriever for Pinecone. @@ -186,7 +187,7 @@ export function configurePineconeRetriever< : index; const response = await scopedIndex.query({ topK: options.k, - vector: queryEmbeddings, + vector: queryEmbeddings[0].embedding, includeValues: false, includeMetadata: true, }); @@ -196,9 +197,14 @@ export function configurePineconeRetriever< .filter((m): m is RecordMetadata => !!m) .map((m) => { const metadata = m; - const content = metadata[contentKey] as string; - delete metadata[contentKey]; - return Document.fromText(content, metadata).toJSON(); + return Document.fromData( + metadata[contentKey] as string, + metadata[CONTENT_TYPE] as string, + JSON.parse(metadata.docMetadata as string) as Record< + string, + unknown + > + ); }), }; } @@ -264,19 +270,34 @@ export function configurePineconeIndexer< ) ); await scopedIndex.upsert( - embeddings.map((value, i) => { - const metadata: RecordMetadata = { - ...docs[i].metadata, - }; + embeddings + .map((value, i) => { + const doc = docs[i]; + // The array of embeddings for this document + const docEmbeddings: Embedding[] = value; - metadata[contentKey] = docs[i].text; - const id = Md5.hashStr(JSON.stringify(docs[i])); - return { - id, - values: value, - metadata, - }; - }) + // Create one doc per docEmbedding so we can store them 1:1. + // They should be unique because the embedding metadata is + // added to the new docs. + const embeddingDocs = doc.getEmbeddingDocuments(docEmbeddings); + + return docEmbeddings.map((docEmbedding, j) => { + const metadata: RecordMetadata = { + docMetadata: JSON.stringify(embeddingDocs[j].metadata), + }; + metadata[contentKey] = embeddingDocs[j].data; + metadata[CONTENT_TYPE] = embeddingDocs[j].dataType || ''; + const id = Md5.hashStr(JSON.stringify(embeddingDocs[j])); + return { + id, + values: docEmbedding.embedding, + metadata, + }; + }); + }) + .reduce((acc, val) => { + return acc.concat(val); + }, []) ); } ); @@ -315,7 +336,7 @@ export async function describePineconeIndex(params: { } /** - * Helper function for deleting Chroma collections. + * Helper function for deleting pinecone indices. * @param params The params for deleting a Pinecone index. * @param params.clientParams The params to initialize Pinecone. * @param params.name The name of the Pinecone index to delete. diff --git a/js/plugins/vertexai/src/embedder.ts b/js/plugins/vertexai/src/embedder.ts index 8e81599ac..032da1c8b 100644 --- a/js/plugins/vertexai/src/embedder.ts +++ b/js/plugins/vertexai/src/embedder.ts @@ -14,8 +14,12 @@ * limitations under the License. */ -import { Genkit, z } from 'genkit'; -import { EmbedderReference, embedderRef } from 'genkit/embedder'; +import { Document, Genkit, z } from 'genkit'; +import { + EmbedderAction, + EmbedderReference, + embedderRef, +} from 'genkit/embedder'; import { GoogleAuth } from 'google-auth-library'; import { PluginOptions } from './common/types.js'; import { PredictClient, predictModel } from './predict.js'; @@ -32,8 +36,8 @@ export type TaskType = z.infer; export const VertexEmbeddingConfigSchema = z.object({ /** - * The `task_type` parameter is defined as the intended downstream application to help the model - * produce better quality embeddings. + * The `task_type` parameter is defined as the intended downstream application + * to help the model produce better quality embeddings. **/ taskType: TaskTypeSchema.optional(), title: z.string().optional(), @@ -51,9 +55,11 @@ export const VertexEmbeddingConfigSchema = z.object({ export type VertexEmbeddingConfig = z.infer; +type InputType = 'text' | 'image' | 'video'; + function commonRef( name: string, - input?: ('text' | 'image')[] + input?: InputType[] ): EmbedderReference { return embedderRef({ name: `vertexai/${name}`, @@ -77,6 +83,11 @@ export const textEmbeddingGeckoMultilingual001 = commonRef( export const textMultilingualEmbedding002 = commonRef( 'text-multilingual-embedding-002' ); +export const multimodalEmbedding001 = commonRef('multimodalembedding@001', [ + 'text', + 'image', + 'video', +]); export const SUPPORTED_EMBEDDER_MODELS: Record = { 'textembedding-gecko@003': textEmbeddingGecko003, @@ -84,19 +95,91 @@ export const SUPPORTED_EMBEDDER_MODELS: Record = { 'text-embedding-005': textEmbedding005, 'textembedding-gecko-multilingual@001': textEmbeddingGeckoMultilingual001, 'text-multilingual-embedding-002': textMultilingualEmbedding002, - // TODO: add support for multimodal embeddings - // 'multimodalembedding@001': commonRef('multimodalembedding@001', [ - // 'image', - // 'text', - // ]), + 'multimodalembedding@001': multimodalEmbedding001, }; -interface EmbeddingInstance { +// https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/multimodal-embeddings-api#request_body +interface MultimodalEmbeddingInstance { + text?: string; + image?: { + // Union field can only be one of the following: + bytesBase64Encoded?: string; + gcsUri?: string; + // End of list of possible types for union field. + mimeType?: string; + }; + video?: { + // Union field can only be one of the following: + bytesBase64Encoded?: string; + gcsUri?: string; + // End of list of possible types for union field. + videoSegmentConfig?: { + startOffsetSec: number; + endOffsetSec: number; + intervalSec: number; + }; + }; + parameters?: { + dimension: number; + }; +} + +interface VideoEmbedding { + startOffsetSec: number; + endOffsetSec: number; + embedding: number[]; +} + +interface MultimodalEmbeddingPrediction { + textEmbedding?: number[]; + imageEmbedding?: number[]; + videoEmbeddings?: VideoEmbedding[]; +} + +function isObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null; +} + +function isMultimodalEmbeddingPrediction( + value: unknown +): value is MultimodalEmbeddingPrediction { + if (!isObject(value)) { + return false; + } + if (!value.textEmbedding && !value.imageEmbedding && !value.videoEmbeddings) { + return false; + } + if (value.textEmbedding && !Array.isArray(value.textEmbedding)) { + return false; + } + if (value.imageEmbedding && !Array.isArray(value.imageEmbedding)) { + return false; + } + if (value.videoEmbeddings && !Array.isArray(value.videoEmbeddings)) { + return false; + } + if (value.videoEmbeddings) { + for (const emb of value.videoEmbeddings as Array) { + if (!isObject(emb)) { + return false; + } + if (!emb.embedding || !Array.isArray(emb.embedding)) { + return false; + } + } + } + + return true; +} + +// https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text-embeddings-api#request_body +interface TextEmbeddingInstance { task_type?: TaskType; content: string; title?: string; } -interface EmbeddingPrediction { + +interface TextEmbeddingPrediction { embeddings: { statistics: { truncated: boolean; @@ -106,12 +189,64 @@ interface EmbeddingPrediction { }; } +type EmbeddingInstance = TextEmbeddingInstance | MultimodalEmbeddingInstance; +type EmbeddingPrediction = + | TextEmbeddingPrediction + | MultimodalEmbeddingPrediction; + +function isMultiModal(embedder: EmbedderReference): boolean { + const input = embedder.info?.supports?.input || ''; + return (input.includes('text') && input.includes('image')) || false; +} + +/** + * Determines if a document is valid for a particular embedder or not. + * This is only used for multimodal embedders. + * @param embedder the embedder name e.g. 'vertexai/multimodalembedding@001' + * @param doc The document to check + */ +function checkValidDocument( + embedder: EmbedderReference, + doc: Document +): boolean { + const isTextOnly = doc.text && doc.media.length == 0; + const isSingleMediaOnly = !doc.text && doc.media.length == 1; + if (isMultiModal(embedder)) { + if (embedder.name == 'vertexai/multimodalembedding@001') { + // We restrict which Document structure can be sent for this embedder because + // while it could accept multiple text and image and video parts in a single + // Document, it would return separate embeddings for each of those parts, + // essentially just batching them. This is not consistent with our "one + // embedding per Document" design. Since the same batching can be achieved by + // sending multiple Documents with one part each, there seems to be no reason + // to change the design. + + if (!isTextOnly && !isSingleMediaOnly) { + throw new Error( + 'Documents for multimodalembedding@001 must be either only text or a single media part.' + ); + } + return true; + } + throw new Error('Unknown multimodal embedder: ' + embedder.name); + } else { + // Not multimodal - unexpected usage. + // Currently text-only embedders just ignore media. + throw new Error('Not implemented'); + } +} + +type EmbeddingResult = { + embedding: number[]; + metadata?: Record; +}; + export function defineVertexAIEmbedder( ai: Genkit, name: string, client: GoogleAuth, options: PluginOptions -) { +): EmbedderAction { const embedder = SUPPORTED_EMBEDDER_MODELS[name]; const predictClients: Record< string, @@ -122,7 +257,8 @@ export function defineVertexAIEmbedder( ): PredictClient => { const requestLocation = config?.location || options.location; if (!predictClients[requestLocation]) { - // TODO: Figure out how to allow different versions while still sharing a single implementation. + // TODO: Figure out how to allow different versions while still + // sharing a single implementation. predictClients[requestLocation] = predictModel< EmbeddingInstance, EmbeddingPrediction @@ -147,19 +283,122 @@ export function defineVertexAIEmbedder( async (input, options) => { const predictClient = predictClientFactory(options); const response = await predictClient( - input.map((i) => { - return { - content: i.text, - task_type: options?.taskType, - title: options?.title, - }; + input.map((doc: Document) => { + let instance: EmbeddingInstance; + if (isMultiModal(embedder) && checkValidDocument(embedder, doc)) { + instance = {}; + if (doc.text) { + instance.text = doc.text; + } + for (var media of doc.media) { + if ( + isObject(media) && + typeof media.url === 'string' && + typeof media.contentType === 'string' + ) { + if (media.contentType?.startsWith('image/')) { + if ( + media.url.startsWith('http') || + media.url.startsWith('gs://') + ) { + instance.image = { + gcsUri: media.url, + mimeType: media.contentType, + }; + } else { + instance.image = { + bytesBase64Encoded: media.url, + mimeType: media.contentType, + }; + } + } else if (media.contentType.startsWith('video/')) { + if ( + media.url.startsWith('http') || + media.url.startsWith('gs://') + ) { + instance.video = { + gcsUri: media.url, + }; + } else { + instance.video = { + bytesBase64Encoded: media.url, + }; + } + if ( + instance.video && + doc.metadata && + doc.metadata.videoSegmentConfig + ) { + instance.video.videoSegmentConfig = + doc.metadata.videoSegmentConfig; + } + } else { + throw new Error( + `Unsupported contentType: '${media.contentType}` + ); + } + } else { + // It needs to be a {url:string, contentType:string} object. + throw new Error('Invalid media specified.'); + } + } + } else { + // Text only embedder + instance = { + content: doc.text, + task_type: options?.taskType, + title: options?.title, + }; + } + return instance; }), { outputDimensionality: options?.outputDimensionality } ); return { - embeddings: response.predictions.map((p) => ({ - embedding: p.embeddings.values, - })), + embeddings: response.predictions + .map((p: EmbeddingPrediction) => { + if (isMultimodalEmbeddingPrediction(p)) { + const eArray: EmbeddingResult[] = []; + if (p.imageEmbedding?.length) { + const imageResult: EmbeddingResult = { + embedding: p.imageEmbedding, + metadata: { embedType: 'imageEmbedding' }, + }; + eArray.push(imageResult); + } + if (p.textEmbedding?.length) { + const textResult: EmbeddingResult = { + embedding: p.textEmbedding, + metadata: { embedType: 'textEmbedding' }, + }; + eArray.push(textResult); + } + if (p.videoEmbeddings?.length) { + for (const ve of p.videoEmbeddings) { + if (ve.embedding?.length) { + const { embedding, ...metadata } = ve; + (metadata as Record).embedType = + 'videoEmbedding'; + const videoResult: EmbeddingResult = { + embedding, + metadata, + }; + eArray.push(videoResult); + } + } + } + return eArray; + } else { + return [ + { + embedding: p.embeddings.values, + }, + ]; + } + }) + .reduce((accumulator, value) => { + return accumulator.concat(value); + }, []), }; } ); diff --git a/js/plugins/vertexai/src/gemini.ts b/js/plugins/vertexai/src/gemini.ts index 60b5abc36..b43613ddb 100644 --- a/js/plugins/vertexai/src/gemini.ts +++ b/js/plugins/vertexai/src/gemini.ts @@ -401,10 +401,10 @@ const toGeminiTool = ( const toGeminiFileDataPart = (part: MediaPart): GeminiPart => { const media = part.media; - if (media.url.startsWith('gs://')) { + if (media.url.startsWith('gs://') || media.url.startsWith('http')) { if (!media.contentType) throw new Error( - 'Must supply contentType when using media from gs:// URLs.' + 'Must supply contentType when using media from http(s):// or gs:// URLs.' ); return { fileData: { diff --git a/js/plugins/vertexai/src/index.ts b/js/plugins/vertexai/src/index.ts index 47de7c9a0..e9f4da922 100644 --- a/js/plugins/vertexai/src/index.ts +++ b/js/plugins/vertexai/src/index.ts @@ -27,6 +27,7 @@ import { PluginOptions } from './common/types.js'; import { SUPPORTED_EMBEDDER_MODELS, defineVertexAIEmbedder, + multimodalEmbedding001, textEmbedding004, textEmbedding005, textEmbeddingGecko003, @@ -61,6 +62,7 @@ export { imagen2, imagen3, imagen3Fast, + multimodalEmbedding001, textEmbedding004, textEmbedding005, textEmbeddingGecko003, diff --git a/js/plugins/vertexai/src/predict.ts b/js/plugins/vertexai/src/predict.ts index 93bf8277c..dd068eed0 100644 --- a/js/plugins/vertexai/src/predict.ts +++ b/js/plugins/vertexai/src/predict.ts @@ -23,8 +23,11 @@ function endpoint(options: { location: string; model: string; }) { - // eslint-disable-next-line max-len - return `https://${options.location}-aiplatform.googleapis.com/v1/projects/${options.projectId}/locations/${options.location}/publishers/google/models/${options.model}:predict`; + return ( + `https://${options.location}-aiplatform.googleapis.com/v1/` + + `projects/${options.projectId}/locations/${options.location}/` + + `publishers/google/models/${options.model}:predict` + ); } interface PredictionResponse { diff --git a/js/plugins/vertexai/src/vectorsearch/vector_search/retrievers.ts b/js/plugins/vertexai/src/vectorsearch/vector_search/retrievers.ts index ca9975a40..b5c8ec81e 100644 --- a/js/plugins/vertexai/src/vectorsearch/vector_search/retrievers.ts +++ b/js/plugins/vertexai/src/vectorsearch/vector_search/retrievers.ts @@ -69,7 +69,7 @@ export function vertexAiRetrievers( embedder: embedderReference, options: embedderOptions, content, - }); + })[0].embedding; // Single embedding for text const accessToken = await params.authClient.getAccessToken(); diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 1a13025d3..da267cb6d 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -1343,6 +1343,76 @@ importers: specifier: ^5.3.3 version: 5.5.3 + testapps/multimodal: + dependencies: + '@genkit-ai/ai': + specifier: workspace:* + version: link:../../ai + '@genkit-ai/dev-local-vectorstore': + specifier: workspace:* + version: link:../../plugins/dev-local-vectorstore + '@genkit-ai/evaluator': + specifier: workspace:* + version: link:../../plugins/evaluators + '@genkit-ai/express': + specifier: workspace:* + version: link:../../plugins/express + '@genkit-ai/firebase': + specifier: workspace:* + version: link:../../plugins/firebase + '@genkit-ai/googleai': + specifier: workspace:* + version: link:../../plugins/googleai + '@genkit-ai/vertexai': + specifier: workspace:* + version: link:../../plugins/vertexai + file-type-checker: + specifier: ^1.1.2 + version: 1.1.2 + genkit: + specifier: workspace:* + version: link:../../genkit + genkitx-chromadb: + specifier: workspace:* + version: link:../../plugins/chroma + genkitx-langchain: + specifier: workspace:* + version: link:../../plugins/langchain + genkitx-pinecone: + specifier: workspace:* + version: link:../../plugins/pinecone + google-auth-library: + specifier: ^9.6.3 + version: 9.14.2(encoding@0.1.13) + llm-chunk: + specifier: ^0.0.1 + version: 0.0.1 + pdf-lib: + specifier: ^1.17.1 + version: 1.17.1 + pdf-parse: + specifier: ^1.1.1 + version: 1.1.1 + devDependencies: + '@types/pdf-parse': + specifier: ^1.1.4 + version: 1.1.4 + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + rimraf: + specifier: ^6.0.1 + version: 6.0.1 + tsx: + specifier: ^4.19.1 + version: 4.19.2 + typescript: + specifier: ^5.3.3 + version: 5.6.3 + vertexai: + specifier: link:@types/@genkit-ai/vertexai + version: link:@types/@genkit-ai/vertexai + testapps/ollama: dependencies: genkit: @@ -3334,6 +3404,12 @@ packages: peerDependencies: '@opentelemetry/api': ^1.1.0 + '@pdf-lib/standard-fonts@1.0.0': + resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==} + + '@pdf-lib/upng@1.0.1': + resolution: {integrity: sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==} + '@pinecone-database/pinecone@2.2.0': resolution: {integrity: sha512-qfVs9n5YyTmerIV1GE1u89xF1W3oFSF53STW68Oqyxey0dGq4775cCw8G5pnwoy872uqfh+tMRDME9bcWfinUw==} engines: {node: '>=14.0.0'} @@ -4411,6 +4487,9 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} + file-type-checker@1.1.2: + resolution: {integrity: sha512-HodBNiinBQNHQfXhXzAuHkU2udHF3LFS6PAOEZqxW+BjotZVCaMR7ckpTTnvLi718dbzRavnjRX0kbSb5pJG3g==} + filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} @@ -5680,6 +5759,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parents@1.0.1: resolution: {integrity: sha512-mXKF3xkoUt5td2DoxpLmtOmZvko9VfFpwRwkKDHSNvgmpLAeBo18YDhcPbBzJq+QLCHMbGOfzia2cX4U+0v9Mg==} @@ -5739,6 +5821,9 @@ packages: path@0.12.7: resolution: {integrity: sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==} + pdf-lib@1.17.1: + resolution: {integrity: sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==} + pdf-parse@1.1.1: resolution: {integrity: sha512-v6ZJ/efsBpGrGGknjtq9J/oC8tZWq0KWL5vQrk2GlzLEQPUDB1ex+13Rmidl1neNN358Jn9EHZw5y07FFtaC7A==} engines: {node: '>=6.8.1'} @@ -6335,6 +6420,9 @@ packages: '@swc/wasm': optional: true + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} @@ -8619,6 +8707,14 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) + '@pdf-lib/standard-fonts@1.0.0': + dependencies: + pako: 1.0.11 + + '@pdf-lib/upng@1.0.1': + dependencies: + pako: 1.0.11 + '@pinecone-database/pinecone@2.2.0': dependencies: '@sinclair/typebox': 0.29.6 @@ -9849,6 +9945,8 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + file-type-checker@1.1.2: {} + filelist@1.0.4: dependencies: minimatch: 5.1.6 @@ -11523,6 +11621,8 @@ snapshots: package-json-from-dist@1.0.1: {} + pako@1.0.11: {} + parents@1.0.1: dependencies: path-platform: 0.11.15 @@ -11577,6 +11677,13 @@ snapshots: process: 0.11.10 util: 0.10.4 + pdf-lib@1.17.1: + dependencies: + '@pdf-lib/standard-fonts': 1.0.0 + '@pdf-lib/upng': 1.0.1 + pako: 1.0.11 + tslib: 1.14.1 + pdf-parse@1.1.1: dependencies: debug: 3.2.7 @@ -12293,6 +12400,8 @@ snapshots: yn: 3.1.1 optional: true + tslib@1.14.1: {} + tslib@2.6.2: {} tsup@8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1): diff --git a/js/testapps/multimodal/docs/BirthdayPets.pdf b/js/testapps/multimodal/docs/BirthdayPets.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b2f0cc3091cfd044e05dfbe0d7ecef20f421819f GIT binary patch literal 254347 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 Jm6NsNzX6|g9d`f# literal 0 HcmV?d00001 diff --git a/js/testapps/multimodal/package.json b/js/testapps/multimodal/package.json new file mode 100644 index 000000000..b0e883538 --- /dev/null +++ b/js/testapps/multimodal/package.json @@ -0,0 +1,44 @@ +{ + "name": "multimodal", + "version": "1.0.0", + "description": "", + "main": "lib/index.js", + "scripts": { + "start": "node lib/index.js", + "dev": "tsx --watch src/index.ts", + "genkit:dev": "cross-env GENKIT_ENV=dev pnpm dev", + "compile": "tsc", + "build": "pnpm build:clean && pnpm compile", + "build:clean": "rimraf ./lib", + "build:watch": "tsc --watch" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@genkit-ai/dev-local-vectorstore": "workspace:*", + "@genkit-ai/evaluator": "workspace:*", + "@genkit-ai/express": "workspace:*", + "@genkit-ai/firebase": "workspace:*", + "@genkit-ai/googleai": "workspace:*", + "@genkit-ai/vertexai": "workspace:*", + "@genkit-ai/ai": "workspace:*", + "file-type-checker": "^1.1.2", + "genkit": "workspace:*", + "genkitx-chromadb": "workspace:*", + "genkitx-langchain": "workspace:*", + "genkitx-pinecone": "workspace:*", + "google-auth-library": "^9.6.3", + "llm-chunk": "^0.0.1", + "pdf-lib": "^1.17.1", + "pdf-parse": "^1.1.1" + }, + "devDependencies": { + "@types/pdf-parse": "^1.1.4", + "cross-env": "^7.0.3", + "rimraf": "^6.0.1", + "tsx": "^4.19.1", + "typescript": "^5.3.3", + "vertexai": "link:@types/@genkit-ai/vertexai" + } +} diff --git a/js/testapps/multimodal/src/genkit.ts b/js/testapps/multimodal/src/genkit.ts new file mode 100644 index 000000000..ca1c52eba --- /dev/null +++ b/js/testapps/multimodal/src/genkit.ts @@ -0,0 +1,38 @@ +/** + * Copyright 2024 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 { devLocalVectorstore } from '@genkit-ai/dev-local-vectorstore'; +import { + gemini15Flash, + multimodalEmbedding001, + vertexAI, +} from '@genkit-ai/vertexai'; +import { genkit } from 'genkit'; + +export const ai = genkit({ + plugins: [ + vertexAI({ + location: 'us-central1', + }), + devLocalVectorstore([ + { + indexName: 'multiModalIndex', + embedder: multimodalEmbedding001, + }, + ]), + ], + model: gemini15Flash, +}); diff --git a/js/testapps/multimodal/src/index.ts b/js/testapps/multimodal/src/index.ts new file mode 100644 index 000000000..4c12e497a --- /dev/null +++ b/js/testapps/multimodal/src/index.ts @@ -0,0 +1,18 @@ +/** + * Copyright 2024 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. + */ + +export * from './pdf.js'; +export * from './video.js'; diff --git a/js/testapps/multimodal/src/pdf.ts b/js/testapps/multimodal/src/pdf.ts new file mode 100644 index 000000000..2b4e878b9 --- /dev/null +++ b/js/testapps/multimodal/src/pdf.ts @@ -0,0 +1,182 @@ +/** + * Copyright 2024 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 { + devLocalIndexerRef, + devLocalRetrieverRef, +} from '@genkit-ai/dev-local-vectorstore'; +import { startFlowServer } from '@genkit-ai/express'; +import fileTypeChecker from 'file-type-checker'; +import fs from 'fs'; +import { Document, z } from 'genkit'; +import { chunk } from 'llm-chunk'; +import path from 'path'; +import { PDFDocument, PDFRawStream } from 'pdf-lib'; +import pdf from 'pdf-parse'; + +import { ai } from './genkit.js'; +import { augmentedMultimodalPrompt } from './prompt.js'; +//import { ExecutablePrompt } from '@genkit-ai/ai'; + +export const pdfMultimodalRetriever = devLocalRetrieverRef('multiModalIndex'); + +export const pdfMultimodalIndexer = devLocalIndexerRef('multiModalIndex'); + +// Define a multimodal PDF QA flow +// (Index a PDF first) +export const multimodalPdfQAFlow = ai.defineFlow( + { + name: 'multimodalPdfQuestions', + inputSchema: z.string(), + outputSchema: z.string(), + }, + async (query: any, streamingCallback: any) => { + const docs = (await ai.retrieve({ + retriever: pdfMultimodalRetriever, + query, + options: { k: 3 }, + })) as Document[]; + + return augmentedMultimodalPrompt( + { + question: query, + text: docs.filter((d) => d.text?.length).map((d) => d.text), + media: docs + .filter( + (d) => d.media[0]?.url?.length && d.media[0]?.contentType?.length + ) + .map((d) => { + if ( + d.media[0].url?.startsWith('gs://') || + d.media[0].url?.startsWith('http') + ) { + return { + gcsUrl: d.media[0]?.url, + contentType: d.media[0]?.contentType, + }; + } + return { + dataUrl: makeDataUrl(d.media[0]), + }; + }), + }, + { + streamingCallback, + } + ).then((r) => r.text); + } +); + +function isObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null; +} + +function makeDataUrl(media: unknown) { + if (isObject(media)) { + if ( + typeof media.contentType === 'string' && + media.contentType.length > 0 && + typeof media.url === 'string' && + media.url.length > 0 + ) { + return `data:${media.contentType};base64,${media.url}`; + } else { + throw new Error( + 'Failed to make data URL. Invalid or missing contentType or url' + ); + } + } + throw new Error( + 'Failed to make data URL. Unexpected media type: ' + typeof media + ); +} + +const chunkingConfig = { + minLength: 800, // number of minimum characters into chunk + maxLength: 1000, // number of maximum characters into chunk + splitter: 'sentence', // paragraph | sentence + overlap: 100, // number of overlap chracters + delimiters: '', // regex for base split method +} as any; + +// Define a flow to index documents into the "vector store" +// genkit flow:run indexMultimodalPdf '"./docs/BirthdayPets.pdf"' +export const indexMultimodalPdf = ai.defineFlow( + { + name: 'indexMultimodalPdf', + inputSchema: z.string().describe('PDF file path'), + }, + async (filePath: string) => { + let documents: Document[] = []; + if (filePath.startsWith('gs://') || filePath.startsWith('http')) { + // non local file, use url for pdf file + // e.g gs://cloud-samples-data/generative-ai/pdf/2403.05530.pdf + documents = [Document.fromMedia(filePath, 'application/pdf')]; + } else { + // local file (e.g. ./docs/BirthdayPets.pdf) + // use data URLs for images + filePath = path.resolve(filePath); + const pdfTxt = await ai.run('extract-text', () => extractText(filePath)); + + const chunks = await ai.run('chunk-it', async () => + chunk(pdfTxt, chunkingConfig) + ); + + const imageDocs = await ai.run('extract-images', () => + extractImages(filePath) + ); + + const textDocs: Document[] = chunks.map((text: string) => { + return Document.fromText(text, { filePath }); + }); + documents = imageDocs.concat(textDocs); + } + + await ai.index({ + indexer: pdfMultimodalIndexer, + documents, + }); + } +); + +async function extractImages(filePath: string): Promise { + const imgDocs: Document[] = []; + const pdfDoc = await PDFDocument.load(fs.readFileSync(filePath)); + const indirectObjects = pdfDoc.context.enumerateIndirectObjects(); + for (const [ref, obj] of indirectObjects) { + if (obj instanceof PDFRawStream) { + const detectedFileInfo = fileTypeChecker.detectFile(obj.contents); + if ( + detectedFileInfo?.mimeType && + detectedFileInfo?.mimeType.startsWith('image/') + ) { + const base64 = Buffer.from(obj.contents).toString('base64'); + imgDocs.push(Document.fromMedia(base64, detectedFileInfo.mimeType)); + } + } + } + return imgDocs; +} + +async function extractText(filePath: string) { + const dataBuffer = fs.readFileSync(filePath); + const data = await pdf(dataBuffer); + return data.text; +} + +startFlowServer({ + flows: [indexMultimodalPdf, multimodalPdfQAFlow], +}); diff --git a/js/testapps/multimodal/src/prompt.ts b/js/testapps/multimodal/src/prompt.ts new file mode 100644 index 000000000..df2704107 --- /dev/null +++ b/js/testapps/multimodal/src/prompt.ts @@ -0,0 +1,91 @@ +/** + * Copyright 2024 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 { gemini15Flash } from '@genkit-ai/vertexai'; +import { z } from 'genkit'; +import { ai } from './genkit.js'; + +export const augmentedVideoPrompt = ai.definePrompt({ + model: gemini15Flash, + name: 'augmentedVideoPrompt', + input: { + schema: z.object({ + question: z.string(), + media: z.object({ + gcsUrl: z.string(), + contentType: z.string(), + startOffsetSec: z.number(), + endOffsetSec: z.number(), + }), + }), + }, + output: { + format: 'text', + }, + messages: ` + Use the following video to answer the question at the end. + If you don't know the answer, just say that you don't know, don't try to make up an answer. + + {{media contentType=media.contentType url=media.gcsUrl}} + + Question: {{question}} + Helpful Answer: `, +}); + +// Define a prompt that includes the retrieved context documents +export const augmentedMultimodalPrompt = ai.definePrompt({ + model: gemini15Flash, + name: 'augmentedMultimodalPrompt', + input: { + schema: z.object({ + text: z.optional(z.array(z.string())), + media: z.optional( + z.array( + z + .object({ + dataUrl: z.string(), + gcsUrl: z.string(), + contentType: z.string(), + }) + .partial() + .refine((data) => data.dataUrl || (data.gcsUrl && data.contentType)) + ) + ), + question: z.string(), + }), + }, + output: { + format: 'text', + }, + messages: ` + Use the following context to answer the question at the end. + If you don't know the answer, just say that you don't know, don't try to make up an answer. + {{#each text}} + - {{this}} + {{/each}} + {{#each media}} + {{#if this.dataUrl}} + {{media url=this.dataUrl}} + {{/if}} + {{#if this.gcsUrl}} + {{media contentType=this.contentType url=this.gcsUrl}} + {{/if}} + {{/each}} + + Question: {{question}} + Helpful Answer: + `, +}); diff --git a/js/testapps/multimodal/src/video.ts b/js/testapps/multimodal/src/video.ts new file mode 100644 index 000000000..fe50d42cb --- /dev/null +++ b/js/testapps/multimodal/src/video.ts @@ -0,0 +1,173 @@ +/** + * Copyright 2024 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 { + devLocalIndexerRef, + devLocalRetrieverRef, +} from '@genkit-ai/dev-local-vectorstore'; +import fileTypeChecker from 'file-type-checker'; +import fs from 'fs'; +import { Document, z } from 'genkit'; +import path from 'path'; +import { ai } from './genkit.js'; +import { augmentedVideoPrompt } from './prompt.js'; + +// Simple aliases for readability +export const videoRetriever = devLocalRetrieverRef('multiModalIndex'); +export const videoIndexer = devLocalIndexerRef('multiModalIndex'); + +// Define a video indexer flow +export const indexVideo = ai.defineFlow( + { + name: 'indexVideo', + inputSchema: z + .string() + .describe( + `Video URL. e.g. 'gs://cloud-samples-data/generative-ai/video/pixel8.mp4'` + ), + }, + async (videoUrl: string) => { + const documents = await ai.run('extract-video', () => + extractVideo(videoUrl) + ); + + await ai.index({ + indexer: videoIndexer, + documents, + }); + } +); + +// Suffix based type +function getVideoType(url: string) { + const lastDotIndex = url.lastIndexOf('.'); + if (lastDotIndex === -1) { + throw new Error('Error: Unable to determine video mime type'); + } + const suffix = url.substring(lastDotIndex + 1); + return `video/${suffix}`; +} + +async function extractVideo(filePath: string): Promise { + const videoDocs: Document[] = []; + + if (filePath.startsWith('http')) { + throw new Error( + 'Vertex AI does not support http(s) video urls. Please use Google Cloud Storage (gs://) urls' + ); + } else if (filePath.startsWith('gs://')) { + // The default configuration is to look at the first 120 seconds of the + // video and produce embeddings in 16 second increments. + // This is not really necessary, since we are very close to the defaults + // (i.e. 15 v.s. 16 seconds) it is just here to show what it looks like. + // See also: + // https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-multimodal-embeddings#video-modes + // for pricing differences for the different intervals. + const metadataFirst120Seconds = { + videoSegmentConfig: { + startOffsetSec: 0, + endOffsetSec: 120, + intervalSec: 15, + }, + }; + + // If your video is longer than 120 seconds, you can add additional video + // document requests with different start/stop values e.g. + // const metadataNext120Seconds = { + // "videoSegmentConfig": { + // "startOffsetSec": 120, + // "endOffsetSec": 240, + // "intervalSec": 15 + // } + // } + // and then: + // videoDocs.push(Document.fromMedia(filePath, getVideoType(filePath), metadataNext120Seconds)); + // + // sample ~4 minute video: gs://cloud-samples-data/generative-ai/video/google_sustainability.mp4 + + videoDocs.push( + Document.fromMedia( + filePath, + getVideoType(filePath), + metadataFirst120Seconds + ) + ); + + return videoDocs; + } + + // Note, this is valid, but it only works for very very tiny videos. + // Otherwise the API request message size is too big. + // The recommended way to handle video is using a 'gs://' URL + const file = path.resolve(filePath); + const dataBuffer = fs.readFileSync(file); + const detectedFileInfo = fileTypeChecker.detectFile(dataBuffer); + if ( + detectedFileInfo?.mimeType && + detectedFileInfo?.mimeType.startsWith('video/') + ) { + videoDocs.push( + Document.fromMedia( + dataBuffer.toString('base64'), + detectedFileInfo?.mimeType + ) + ); + } else { + throw new Error('Error: Unable to determine mime type of the file.'); + } + return videoDocs; +} + +// Define a video QA flow +export const VideoQAFlow = ai.defineFlow( + { + name: 'videoQuestions', + inputSchema: z.string(), + outputSchema: z.string(), + }, + async (query: any, streamingCallback: any) => { + const docs = (await ai.retrieve({ + retriever: videoRetriever, + query, + options: { k: 1 }, // we are choosing a single segment of video for context + })) as Document[]; + + return augmentedVideoPrompt( + { + question: query, + media: docs + .filter( + (d) => d.media[0]?.url?.length && d.media[0]?.contentType?.length + ) + .map((d) => { + console.log( + `Retriever returned video: ${d.media[0].url} from ${d.metadata?.embedMetadata?.startOffsetSec}s to ${d.metadata?.embedMetadata?.endOffsetSec}s` + ); + return { + gcsUrl: d.media[0]?.url, + contentType: d.media[0]?.contentType || '', + startOffsetSec: d.metadata?.embedMetadata + ?.startOffsetSec as number, + endOffsetSec: d.metadata?.embedMetadata?.endOffsetSec as number, + }; + })[0], + }, + { + streamingCallback, + } + ).then((r) => r.text); + } +); diff --git a/js/testapps/multimodal/tsconfig.json b/js/testapps/multimodal/tsconfig.json new file mode 100644 index 000000000..b73ccd04d --- /dev/null +++ b/js/testapps/multimodal/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", + "noImplicitReturns": true, + "noUnusedLocals": false, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017", + "skipLibCheck": true, + "esModuleInterop": true + }, + "compileOnSave": true, + "include": ["src"] +} diff --git a/js/testapps/ollama/src/index.ts b/js/testapps/ollama/src/index.ts index 3e625f370..5ded9d58f 100644 --- a/js/testapps/ollama/src/index.ts +++ b/js/testapps/ollama/src/index.ts @@ -74,10 +74,12 @@ const pokemonList: PokemonInfo[] = [ // Step 1: Embed each Pokemon's description async function embedPokemon() { for (const pokemon of pokemonList) { - pokemon.embedding = await ai.embed({ - embedder: 'ollama/nomic-embed-text', - content: pokemon.description, - }); + pokemon.embedding = ( + await ai.embed({ + embedder: 'ollama/nomic-embed-text', + content: pokemon.description, + }) + )[0].embedding; } } @@ -107,10 +109,12 @@ function cosineDistance(a: number[], b: number[]) { // Step 3: Generate response with RAG results in context async function generateResponse(question: string) { - const inputEmbedding = await ai.embed({ - embedder: 'ollama/nomic-embed-text', - content: question, - }); + const inputEmbedding = ( + await ai.embed({ + embedder: 'ollama/nomic-embed-text', + content: question, + }) + )[0].embedding; const nearestPokemon = findNearestPokemon(inputEmbedding); const pokemonContext = nearestPokemon diff --git a/js/testapps/rag/src/pdf-rag-firebase.ts b/js/testapps/rag/src/pdf-rag-firebase.ts index 2770858f1..90421e7e6 100644 --- a/js/testapps/rag/src/pdf-rag-firebase.ts +++ b/js/testapps/rag/src/pdf-rag-firebase.ts @@ -134,10 +134,12 @@ export const indexPdfFirebase = ai.defineFlow( async function indexToFirestore(data: string[]) { for (const text of data) { - const embedding = await ai.embed({ - embedder: indexConfig.embedder, - content: text, - }); + const embedding = ( + await ai.embed({ + embedder: indexConfig.embedder, + content: text, + }) + )[0].embedding; await firestore.collection(indexConfig.collection).add({ [indexConfig.vectorField]: FieldValue.vector(embedding), [indexConfig.contentField]: text, From 82ab12520608292de57025c847d46ec2c8be655f Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Wed, 29 Jan 2025 17:52:48 -0500 Subject: [PATCH 389/562] fix(js/ai/prompt): lazy load dotprompt files to avoid initialization race conditions (#1699) --- js/ai/src/prompt.ts | 14 ++++++++++---- js/core/src/action.ts | 4 ++-- js/core/src/async.ts | 26 ++++++++++++++++++++++++++ js/core/src/registry.ts | 4 ++-- js/core/tests/async_test.ts | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 js/core/tests/async_test.ts diff --git a/js/ai/src/prompt.ts b/js/ai/src/prompt.ts index 901b3685c..5eb5cfc35 100644 --- a/js/ai/src/prompt.ts +++ b/js/ai/src/prompt.ts @@ -25,6 +25,7 @@ import { stripUndefinedProps, z, } from '@genkit-ai/core'; +import { LazyPromise } from '@genkit-ai/core/async'; import { logger } from '@genkit-ai/core/logging'; import { Registry } from '@genkit-ai/core/registry'; import { toJsonSchema } from '@genkit-ai/core/schema'; @@ -240,7 +241,7 @@ function definePromptAsync< >( registry: Registry, name: string, - optionsPromise: Promise> + optionsPromise: PromiseLike> ): ExecutablePrompt, O, CustomOptions> { const promptCache = {} as PromptCache; @@ -732,7 +733,12 @@ function loadPrompt( definePromptAsync( registry, registryDefinitionKey(name, variant ?? undefined, ns), - registry.dotprompt.renderMetadata(parsedPrompt).then((promptMetadata) => { + // We use a lazy promise here because we only want prompt loaded when it's first used. + // This is important because otherwise the loading may happen before the user has configured + // all the schemas, etc., which will result in dotprompt.renderMetadata errors. + new LazyPromise(async (resolvePromptConfig) => { + const promptMetadata = + await registry.dotprompt.renderMetadata(parsedPrompt); if (variant) { promptMetadata.variant = variant; } @@ -745,7 +751,7 @@ function loadPrompt( delete promptMetadata.input.schema.description; } - return { + resolvePromptConfig({ name: registryDefinitionKey(name, variant ?? undefined, ns), model: promptMetadata.model, config: promptMetadata.config, @@ -770,7 +776,7 @@ function loadPrompt( toolChoice: promptMetadata.raw?.['toolChoice'], returnToolRequests: promptMetadata.raw?.['returnToolRequests'], messages: parsedPrompt.template, - }; + }); }) ); } diff --git a/js/core/src/action.ts b/js/core/src/action.ts index b04f5c99b..b942c26d1 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -446,8 +446,8 @@ export function defineActionAsync< pluginId: string; actionId: string; }, - config: Promise> -): Promise> { + config: PromiseLike> +): PromiseLike> { const actionName = typeof name === 'string' ? name : `${name.pluginId}/${name.actionId}`; const actionPromise = config.then((resolvedConfig) => { diff --git a/js/core/src/async.ts b/js/core/src/async.ts index 1c42d8ead..2a4d9a05f 100644 --- a/js/core/src/async.ts +++ b/js/core/src/async.ts @@ -87,3 +87,29 @@ export class Channel implements AsyncIterable { }; } } + +/** + * A lazy promise that does not run its executor function until then is called. + */ +export class LazyPromise implements PromiseLike { + private executor; + private promise; + + constructor(executor: (resolve?, reject?) => void | Promise) { + this.executor = executor; + } + + then( + onfulfilled?: + | ((value: T) => TResult1 | PromiseLike) + | undefined + | null, + onrejected?: + | ((reason: any) => TResult2 | PromiseLike) + | undefined + | null + ): PromiseLike { + this.promise ??= new Promise(this.executor); + return this.promise.then(onfulfilled, onrejected); + } +} diff --git a/js/core/src/registry.ts b/js/core/src/registry.ts index 96d95ada7..af9888de3 100644 --- a/js/core/src/registry.ts +++ b/js/core/src/registry.ts @@ -67,7 +67,7 @@ export class Registry { private actionsById: Record< string, | Action - | Promise> + | PromiseLike> > = {}; private pluginsByName: Record = {}; private schemasByName: Record = {}; @@ -145,7 +145,7 @@ export class Registry { registerActionAsync( type: ActionType, name: string, - action: Promise> + action: PromiseLike> ) { const key = `/${type}/${name}`; logger.debug(`registering ${key} (async)`); diff --git a/js/core/tests/async_test.ts b/js/core/tests/async_test.ts new file mode 100644 index 000000000..821d11d5e --- /dev/null +++ b/js/core/tests/async_test.ts @@ -0,0 +1,36 @@ +/** + * Copyright 2024 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 * as assert from 'assert'; +import { describe, it } from 'node:test'; +import { LazyPromise } from '../src/async'; + +describe('LazyPromise', () => { + it('call its function lazily', async () => { + let called = false; + const lazy = new LazyPromise((resolver) => { + called = true; + resolver('foo'); + }); + + assert.ok(!called); + + const result = await lazy; + + assert.ok(called); + assert.equal(result, 'foo'); + }); +}); From 24493164dd20509d3e82ecedfe8ba8a2b1c45996 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Wed, 29 Jan 2025 17:08:06 -0800 Subject: [PATCH 390/562] feat(js): Add @genkit-ai/next plugin (#1676) Does not yet support auth policies et. al. Will propose a big refactor for all of that stuff. --- .gitignore | 2 + js/plugins/next/README.md | 67 +++ js/plugins/next/package.json | 75 +++ js/plugins/next/src/client.ts | 55 ++ js/plugins/next/src/index.ts | 80 +++ js/plugins/next/tsconfig.json | 8 + js/plugins/next/tsup.config.ts | 22 + js/pnpm-lock.yaml | 607 +++++++++++++++++++-- js/testapps/next/package.json | 30 + js/testapps/next/src/app/api/joke/route.ts | 20 + js/testapps/next/src/app/globals.css | 18 + js/testapps/next/src/app/layout.tsx | 32 ++ js/testapps/next/src/app/page.module.css | 5 + js/testapps/next/src/app/page.tsx | 66 +++ js/testapps/next/src/genkit/index.ts | 23 + js/testapps/next/src/genkit/joke.ts | 36 ++ js/testapps/next/tsconfig.json | 33 ++ 17 files changed, 1130 insertions(+), 49 deletions(-) create mode 100644 js/plugins/next/README.md create mode 100644 js/plugins/next/package.json create mode 100644 js/plugins/next/src/client.ts create mode 100644 js/plugins/next/src/index.ts create mode 100644 js/plugins/next/tsconfig.json create mode 100644 js/plugins/next/tsup.config.ts create mode 100644 js/testapps/next/package.json create mode 100644 js/testapps/next/src/app/api/joke/route.ts create mode 100644 js/testapps/next/src/app/globals.css create mode 100644 js/testapps/next/src/app/layout.tsx create mode 100644 js/testapps/next/src/app/page.module.css create mode 100644 js/testapps/next/src/app/page.tsx create mode 100644 js/testapps/next/src/genkit/index.ts create mode 100644 js/testapps/next/src/genkit/joke.ts create mode 100644 js/testapps/next/tsconfig.json diff --git a/.gitignore b/.gitignore index 46736ee1c..17e22060e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ package-lock.json .angular *.tgz .pnpm-store +.next # version file (autogenerated by CLI) genkit-tools/cli/src/utils/version.ts @@ -40,5 +41,6 @@ last_recording.mp4 # auto-generated /js/core/src/__codegen /js/api-refs-js +next-env.d.ts !samples/chatbot/server/genkit-ai-vertexai-*.tgz diff --git a/js/plugins/next/README.md b/js/plugins/next/README.md new file mode 100644 index 000000000..93cb880aa --- /dev/null +++ b/js/plugins/next/README.md @@ -0,0 +1,67 @@ +# Genkit Next.js Plugin + +This plugin provides utilities for conveninetly exposing Genkit flows and actions via Next.js app routs for REST APIs. + +```ts +// /genkit/simpleFlow.ts +const simpleFlow = ai.defineFlow( + 'simpleFlow', + async (input, streamingCallback) => { + const { text } = await ai.generate({ + model: gemini15Flash, + prompt: input, + streamingCallback: (chunk) => streamingCallback(chunk.text), + }); + return text; + } +); +``` + +```ts +// /app/api/simpleFlow/route.ts +import { simpleFlow } from '@/genkit/simpleFlow'; +import { appRoute } from '@genkit-ai/nextjs'; + +export const POST = appRoute(simpleFlow); +``` + +APIs can be called with the generic `genkit/client` library, or `@genkit-ai/nextjs/client` + +```ts +import { runFlow, streamFlow } from '@genkit-ai/nextjs/client'; +import { simpleFlow } from '@/genkit/simpleFlow'; + +const result = await runFlow({ + url: '/api/simpleFlow', + input: 'say hello', +}); + +console.log(result); // hello + +// set auth headers (when using auth policies) +const result = await runFlow({ + url: `/api/simpleFlow`, + headers: { + Authorization: 'open sesame', + }, + input: 'say hello', +}); + +console.log(result); // hello + +// and streamed +const result = streamFlow({ + url: '/api/simpleFlow', + input: 'say hello', +}); +for await (const chunk of result.stream()) { + console.log(chunk.output); +} +console.log(await result.output()); +``` + +The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. + +Usage information and reference details can be found in [Genkit documentation](https://firebase.google.com/docs/genkit). + +License: Apache 2.0 diff --git a/js/plugins/next/package.json b/js/plugins/next/package.json new file mode 100644 index 000000000..267b8ca12 --- /dev/null +++ b/js/plugins/next/package.json @@ -0,0 +1,75 @@ +{ + "name": "@genkit-ai/next", + "description": "Next.js plugin for Genkit", + "keywords": [ + "genkit", + "genkit-plugin", + "google cloud", + "google ai", + "ai", + "genai", + "generative-ai", + "next", + "nextjs", + "next.js", + "react" + ], + "version": "0.0.1-dev.1", + "type": "commonjs", + "main": "lib/index.js", + "scripts": { + "check": "tsc", + "compile": "tsup-node", + "build:clean": "rimraf ./lib", + "build": "npm-run-all build:clean check compile", + "build:watch": "tsup-node --watch" + }, + "repository": { + "type": "git", + "url": "https://github.com/firebase/genkit.git", + "directory": "js/plugins/next" + }, + "author": "genkit", + "license": "Apache-2.0", + "peerDependencies": { + "@genkit-ai/core": "workspace:*", + "genkit": "workspace:*", + "next": "^15.0.0" + }, + "devDependencies": { + "@genkit-ai/core": "workspace:*", + "genkit": "workspace:*", + "@types/node": "^20.11.16", + "@types/react": "^19", + "@types/react-dom": "^19", + "npm-run-all": "^4.1.5", + "rimraf": "^6.0.1", + "tsup": "^8.0.2", + "tsx": "^4.7.0", + "typescript": "^4.9.0", + "next": "^15.0.0", + "zod": "^3.24.1" + }, + "types": "./lib/index.d.ts", + "exports": { + ".": { + "require": "./lib/index.js", + "import": "./lib/index.mjs", + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + }, + "./client": { + "require": "./lib/client.js", + "import": "./lib/client.mjs", + "types": "./lib/client.d.ts", + "default": "./lib/client.js" + } + }, + "typesVersions": { + "*": { + "client": [ + "lib/client" + ] + } + } +} diff --git a/js/plugins/next/src/client.ts b/js/plugins/next/src/client.ts new file mode 100644 index 000000000..be41ae6ec --- /dev/null +++ b/js/plugins/next/src/client.ts @@ -0,0 +1,55 @@ +/** + * 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. + */ + +import { Action } from '@genkit-ai/core'; +import { + runFlow as baseRunFlow, + streamFlow as baseStreamFlow, +} from 'genkit/client'; + +type Input = + A extends Action ? I['_output'] : never; +type Output = + A extends Action ? O['_output'] : never; +type Stream = + A extends Action ? S['_output'] : never; + +export interface RequestData { + url: string; + headers?: Record; + input?: T; +} + +export function runFlow( + req: RequestData> +): Promise> { + return baseRunFlow>(req); +} + +export interface StreamResponse { + output: Promise>; + stream: AsyncIterable>; +} + +export function streamFlow( + req: RequestData> +): StreamResponse { + const res = baseStreamFlow, Stream>(req); + return { + output: res.output, + stream: res.stream, + }; +} diff --git a/js/plugins/next/src/index.ts b/js/plugins/next/src/index.ts new file mode 100644 index 000000000..4d4bb6194 --- /dev/null +++ b/js/plugins/next/src/index.ts @@ -0,0 +1,80 @@ +/** + * 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. + */ + +import type { Action } from '@genkit-ai/core'; +import { NextRequest, NextResponse } from 'next/server.js'; + +const appRoute = + >(action: A) => + async (req: NextRequest): Promise => { + const { data } = await req.json(); + if (req.headers.get('accept') !== 'text/event-stream') { + try { + const resp = await action.run(data); + return NextResponse.json({ result: resp.result }); + } catch (e) { + // For security reasons, log the error rather than responding with it. + console.error(e); + return NextResponse.json({ error: 'INTERNAL' }, { status: 500 }); + } + } + + const { output, stream } = action.stream(data); + const encoder = new TextEncoder(); + const { readable, writable } = new TransformStream(); + + // Not using a dangling Promise causes NextResponse to deadlock. + // TODO: Add ping comments at regular intervals between streaming responses to mitigate + // timeouts. + (async (): Promise => { + const writer = writable.getWriter(); + try { + for await (const chunk of stream) { + console.debug('Writing chunk ' + chunk + '\n'); + await writer.write( + encoder.encode( + 'data: ' + JSON.stringify({ message: chunk }) + '\n\n' + ) + ); + } + await writer.write( + encoder.encode( + 'data: ' + JSON.stringify({ result: await output }) + '\n\n' + ) + ); + await writer.write('END'); + } catch (err) { + console.error('Error in streaming output:', err); + await writer.write( + encoder.encode(`error: {"error": {"message":"INTERNAL"}}` + '\n\n') + ); + await writer.write(encoder.encode('END')); + } + })(); + + return new NextResponse(readable, { + status: 200, + headers: { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + 'Transfer-Encoding': 'chunked', + }, + }); + }; + +export default appRoute; +export { appRoute }; diff --git a/js/plugins/next/tsconfig.json b/js/plugins/next/tsconfig.json new file mode 100644 index 000000000..38a76c6a4 --- /dev/null +++ b/js/plugins/next/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src"], + "compilerOptions": { + // Open telemetry has a metric load of type errors when trying to import React DOM and @vercel/og/types + "skipLibCheck": true + } +} diff --git a/js/plugins/next/tsup.config.ts b/js/plugins/next/tsup.config.ts new file mode 100644 index 000000000..01dce0a6b --- /dev/null +++ b/js/plugins/next/tsup.config.ts @@ -0,0 +1,22 @@ +/** + * Copyright 2024 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 { defineConfig, Options } from 'tsup'; +import { defaultOptions } from '../../tsup.common'; + +export default defineConfig({ + ...(defaultOptions as Options), +}); diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index da267cb6d..32b71ea51 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -674,6 +674,45 @@ importers: specifier: ^5.3.0 version: 5.6.3 + plugins/next: + devDependencies: + '@genkit-ai/core': + specifier: workspace:* + version: link:../../core + '@types/node': + specifier: ^20.11.16 + version: 20.16.9 + '@types/react': + specifier: ^19 + version: 19.0.8 + '@types/react-dom': + specifier: ^19 + version: 19.0.3(@types/react@19.0.8) + genkit: + specifier: workspace:* + version: link:../../genkit + next: + specifier: ^15.0.0 + version: 15.1.6(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + npm-run-all: + specifier: ^4.1.5 + version: 4.1.5 + rimraf: + specifier: ^6.0.1 + version: 6.0.1 + tsup: + specifier: ^8.0.2 + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + tsx: + specifier: ^4.7.0 + version: 4.19.2 + typescript: + specifier: ^4.9.0 + version: 4.9.5 + zod: + specifier: ^3.24.1 + version: 3.24.1 + plugins/ollama: dependencies: genkit: @@ -749,7 +788,7 @@ importers: version: 1.9.2(encoding@0.1.13) '@mistralai/mistralai-gcp': specifier: ^1.3.5 - version: 1.3.5(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.23.8) + version: 1.3.5(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.24.1) genkit: specifier: workspace:* version: link:../../genkit @@ -1334,7 +1373,7 @@ importers: version: link:../../plugins/ollama genkitx-openai: specifier: ^0.10.1 - version: 0.10.1(@genkit-ai/ai@1.0.0-rc.5)(@genkit-ai/core@1.0.0-rc.5) + version: 0.10.1(@genkit-ai/ai@1.0.0-rc.10)(@genkit-ai/core@1.0.0-rc.10) devDependencies: rimraf: specifier: ^6.0.1 @@ -1368,7 +1407,7 @@ importers: version: link:../../plugins/vertexai file-type-checker: specifier: ^1.1.2 - version: 1.1.2 + version: 1.1.3 genkit: specifier: workspace:* version: link:../../genkit @@ -1413,6 +1452,34 @@ importers: specifier: link:@types/@genkit-ai/vertexai version: link:@types/@genkit-ai/vertexai + testapps/next: + dependencies: + '@genkit-ai/googleai': + specifier: workspace:* + version: link:../../plugins/googleai + '@genkit-ai/next': + specifier: workspace:* + version: link:../../plugins/next + genkit: + specifier: workspace:* + version: link:../../genkit + next: + specifier: ^15.1.6 + version: 15.1.6(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + zod: + specifier: ^3.24.1 + version: 3.24.1 + devDependencies: + '@types/react': + specifier: 19.0.8 + version: 19.0.8 + rimraf: + specifier: ^6.0.1 + version: 6.0.1 + typescript: + specifier: ^5.3.3 + version: 5.6.3 + testapps/ollama: dependencies: genkit: @@ -1907,6 +1974,9 @@ packages: '@dabh/diagnostics@2.0.3': resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + '@emnapi/runtime@1.3.1': + resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} + '@esbuild/aix-ppc64@0.23.1': resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} engines: {node: '>=18'} @@ -2435,11 +2505,11 @@ packages: '@firebase/webchannel-wrapper@1.0.3': resolution: {integrity: sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==} - '@genkit-ai/ai@1.0.0-rc.5': - resolution: {integrity: sha512-bNalrwA5HTNa9lo0dbh6I/fCFMbiqMXo1GYNwWbdYEZFyaK11L1sgoAcRoafZQJublvFKnaTn9llrn7VHQkaBA==} + '@genkit-ai/ai@1.0.0-rc.10': + resolution: {integrity: sha512-FIwseXpGFChZAoGXJiSDFSqEUam5dK0SR6EExtukBfZg4BGLz5e0XjzqmPvf8HW3iCWF/3MOZEBx7fVrCW60+g==} - '@genkit-ai/core@1.0.0-rc.5': - resolution: {integrity: sha512-jTu0Flixq27H3dKAwysoUHk5LwX0anMevd2J+0reTK0DCRzVdc0mJjlBw5nX29XszB9zAp+Y/bSOjeCreBW33A==} + '@genkit-ai/core@1.0.0-rc.10': + resolution: {integrity: sha512-KA+z3oqOsL2w65cMchfmS1ont1gkm0WYOQwzm9cxmyjBbbGP1Ndq3udCIUJM410emITuU4wHEDu1dokDoSaOWA==} '@gerrit0/mini-shiki@1.24.4': resolution: {integrity: sha512-YEHW1QeAg6UmxEmswiQbOVEg1CW22b1XUD/lNTliOsu0LD0wqoyleFMnmbTp697QE0pcadQiR5cVtbbAPncvpw==} @@ -2560,6 +2630,111 @@ packages: engines: {node: '>=6'} hasBin: true + '@img/sharp-darwin-arm64@0.33.5': + resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.33.5': + resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.0.4': + resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.0.4': + resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.0.4': + resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.0.5': + resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.0.4': + resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.0.4': + resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.33.5': + resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.33.5': + resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-s390x@0.33.5': + resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.33.5': + resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.33.5': + resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.33.5': + resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.33.5': + resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-ia32@0.33.5': + resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.33.5': + resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -2966,6 +3141,57 @@ packages: '@modelcontextprotocol/sdk@1.0.0': resolution: {integrity: sha512-mbe0otw8vTtZoL5pVucXAmx6oEC7YjdXBgVeFkJXASu4OAnLkrIeNw9zwzU5CwEp19M54bjOUGcna90Dl/H5Bw==} + '@next/env@15.1.6': + resolution: {integrity: sha512-d9AFQVPEYNr+aqokIiPLNK/MTyt3DWa/dpKveiAaVccUadFbhFEvY6FXYX2LJO2Hv7PHnLBu2oWwB4uBuHjr/w==} + + '@next/swc-darwin-arm64@15.1.6': + resolution: {integrity: sha512-u7lg4Mpl9qWpKgy6NzEkz/w0/keEHtOybmIl0ykgItBxEM5mYotS5PmqTpo+Rhg8FiOiWgwr8USxmKQkqLBCrw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@15.1.6': + resolution: {integrity: sha512-x1jGpbHbZoZ69nRuogGL2MYPLqohlhnT9OCU6E6QFewwup+z+M6r8oU47BTeJcWsF2sdBahp5cKiAcDbwwK/lg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@15.1.6': + resolution: {integrity: sha512-jar9sFw0XewXsBzPf9runGzoivajeWJUc/JkfbLTC4it9EhU8v7tCRLH7l5Y1ReTMN6zKJO0kKAGqDk8YSO2bg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@15.1.6': + resolution: {integrity: sha512-+n3u//bfsrIaZch4cgOJ3tXCTbSxz0s6brJtU3SzLOvkJlPQMJ+eHVRi6qM2kKKKLuMY+tcau8XD9CJ1OjeSQQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@15.1.6': + resolution: {integrity: sha512-SpuDEXixM3PycniL4iVCLyUyvcl6Lt0mtv3am08sucskpG0tYkW1KlRhTgj4LI5ehyxriVVcfdoxuuP8csi3kQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@15.1.6': + resolution: {integrity: sha512-L4druWmdFSZIIRhF+G60API5sFB7suTbDRhYWSjiw0RbE+15igQvE2g2+S973pMGvwN3guw7cJUjA/TmbPWTHQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@15.1.6': + resolution: {integrity: sha512-s8w6EeqNmi6gdvM19tqKKWbCyOBvXFbndkGHl+c9YrzsLARRdCHsD9S1fMj8gsXm9v8vhC8s3N8rjuC/XrtkEg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@15.1.6': + resolution: {integrity: sha512-6xomMuu54FAFxttYr5PJbEfu96godcxBTRk1OhAvJq0/EnmFU/Ybiax30Snis4vdWZ9LGpf7Roy5fSs7v/5ROQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@opentelemetry/api-logs@0.52.1': resolution: {integrity: sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==} engines: {node: '>=14'} @@ -3559,6 +3785,12 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@tootallnate/once@2.0.0': resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} @@ -3695,6 +3927,14 @@ packages: '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/react-dom@19.0.3': + resolution: {integrity: sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==} + peerDependencies: + '@types/react': ^19.0.0 + + '@types/react@19.0.8': + resolution: {integrity: sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==} + '@types/request@2.48.12': resolution: {integrity: sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==} @@ -3954,6 +4194,10 @@ packages: peerDependencies: esbuild: '>=0.18' + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -4044,6 +4288,9 @@ packages: cjs-module-lexer@1.2.3: resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -4074,6 +4321,10 @@ packages: color@3.2.1: resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} @@ -4161,6 +4412,9 @@ packages: crypt@0.0.2: resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + data-uri-to-buffer@4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} @@ -4487,8 +4741,8 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} - file-type-checker@1.1.2: - resolution: {integrity: sha512-HodBNiinBQNHQfXhXzAuHkU2udHF3LFS6PAOEZqxW+BjotZVCaMR7ckpTTnvLi718dbzRavnjRX0kbSb5pJG3g==} + file-type-checker@1.1.3: + resolution: {integrity: sha512-SLMNPu0RZEQsfR+GRNnVBlBPdtXn2BTpvSzBRw9MDjDacobK+Vc0WtbQ/mZx7vqNy+b6juKsza5DvEN5i7LwCw==} filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} @@ -5620,6 +5874,27 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + next@15.1.6: + resolution: {integrity: sha512-Hch4wzbaX0vKQtalpXvUiw5sYivBy4cm5rzUKrBnUB/y436LGrvOUqYvlSeNVCWFO/770gDlltR9gqZH62ct4Q==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + node-abi@3.71.0: resolution: {integrity: sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==} engines: {node: '>=10'} @@ -5846,9 +6121,6 @@ packages: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} - picocolors@1.1.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -5899,6 +6171,10 @@ packages: yaml: optional: true + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + postcss@8.4.47: resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} @@ -6134,6 +6410,10 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sharp@0.33.5: + resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -6225,6 +6505,10 @@ packages: stream-shift@1.0.3: resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} @@ -6289,6 +6573,19 @@ packages: stubs@3.0.0: resolution: {integrity: sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==} + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} @@ -6426,6 +6723,9 @@ packages: tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsup@8.3.5: resolution: {integrity: sha512-Tunf6r6m6tnZsG9GYWndg0z8dEV7fD733VBFzFJ5Vcm1FtlXB8xBD/rtrBi2a3YKEV7hHtxiZtW5EAVADoe1pA==} engines: {node: '>=18'} @@ -6752,6 +7052,9 @@ packages: zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + zod@3.24.1: + resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} + snapshots: '@ampproject/remapping@2.3.0': @@ -6797,7 +7100,7 @@ snapshots: '@babel/code-frame@7.25.7': dependencies: '@babel/highlight': 7.25.7 - picocolors: 1.1.0 + picocolors: 1.1.1 '@babel/compat-data@7.25.7': {} @@ -6878,7 +7181,7 @@ snapshots: '@babel/helper-validator-identifier': 7.25.7 chalk: 2.4.2 js-tokens: 4.0.0 - picocolors: 1.1.0 + picocolors: 1.1.1 '@babel/parser@7.25.7': dependencies: @@ -7008,6 +7311,11 @@ snapshots: enabled: 2.0.0 kuler: 2.0.0 + '@emnapi/runtime@1.3.1': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild/aix-ppc64@0.23.1': optional: true @@ -7255,12 +7563,12 @@ snapshots: '@firebase/component@0.6.11': dependencies: '@firebase/util': 1.10.2 - tslib: 2.6.2 + tslib: 2.8.1 '@firebase/component@0.6.6': dependencies: '@firebase/util': 1.9.5 - tslib: 2.6.2 + tslib: 2.8.1 '@firebase/data-connect@0.1.3(@firebase/app@0.10.17)': dependencies: @@ -7278,7 +7586,7 @@ snapshots: '@firebase/database-types': 1.0.2 '@firebase/logger': 0.4.1 '@firebase/util': 1.9.5 - tslib: 2.6.2 + tslib: 2.8.1 '@firebase/database-compat@2.0.1': dependencies: @@ -7317,7 +7625,7 @@ snapshots: '@firebase/logger': 0.4.1 '@firebase/util': 1.9.5 faye-websocket: 0.11.4 - tslib: 2.6.2 + tslib: 2.8.1 '@firebase/firestore-compat@0.3.40(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17)': dependencies: @@ -7396,11 +7704,11 @@ snapshots: '@firebase/logger@0.4.1': dependencies: - tslib: 2.6.2 + tslib: 2.8.1 '@firebase/logger@0.4.4': dependencies: - tslib: 2.6.2 + tslib: 2.8.1 '@firebase/messaging-compat@0.2.15(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17)': dependencies: @@ -7500,7 +7808,7 @@ snapshots: '@firebase/util@1.9.5': dependencies: - tslib: 2.6.2 + tslib: 2.8.1 '@firebase/vertexai@1.0.2(@firebase/app-types@0.9.3)(@firebase/app@0.10.17)': dependencies: @@ -7514,12 +7822,13 @@ snapshots: '@firebase/webchannel-wrapper@1.0.3': {} - '@genkit-ai/ai@1.0.0-rc.5': + '@genkit-ai/ai@1.0.0-rc.10': dependencies: - '@genkit-ai/core': 1.0.0-rc.5 + '@genkit-ai/core': 1.0.0-rc.10 '@opentelemetry/api': 1.9.0 '@types/node': 20.16.9 colorette: 2.0.20 + dotprompt: 1.0.0-dev.3 json5: 2.2.3 node-fetch: 3.3.2 partial-json: 0.1.7 @@ -7527,7 +7836,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@genkit-ai/core@1.0.0-rc.5': + '@genkit-ai/core@1.0.0-rc.10': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) @@ -7541,11 +7850,12 @@ snapshots: async-mutex: 0.5.0 body-parser: 1.20.3 cors: 2.8.5 + dotprompt: 1.0.0-dev.3 express: 4.21.1 get-port: 5.1.0 json-schema: 0.4.0 - zod: 3.23.8 - zod-to-json-schema: 3.22.5(zod@3.23.8) + zod: 3.24.1 + zod-to-json-schema: 3.22.5(zod@3.24.1) transitivePeerDependencies: - supports-color @@ -7770,6 +8080,81 @@ snapshots: protobufjs: 7.3.2 yargs: 17.7.2 + '@img/sharp-darwin-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.4 + optional: true + + '@img/sharp-darwin-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.0.5': + optional: true + + '@img/sharp-libvips-linux-s390x@1.0.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + optional: true + + '@img/sharp-linux-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.4 + optional: true + + '@img/sharp-linux-arm@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.5 + optional: true + + '@img/sharp-linux-s390x@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.4 + optional: true + + '@img/sharp-linux-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + optional: true + + '@img/sharp-wasm32@0.33.5': + dependencies: + '@emnapi/runtime': 1.3.1 + optional: true + + '@img/sharp-win32-ia32@0.33.5': + optional: true + + '@img/sharp-win32-x64@0.33.5': + optional: true + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -8019,8 +8404,8 @@ snapshots: flat: 5.0.2 langsmith: 0.1.14 uuid: 9.0.1 - zod: 3.23.8 - zod-to-json-schema: 3.22.5(zod@3.23.8) + zod: 3.24.1 + zod-to-json-schema: 3.22.5(zod@3.24.1) optionalDependencies: '@pinecone-database/pinecone': 2.2.0 chromadb: 1.9.2(encoding@0.1.13)(openai@4.53.0(encoding@0.1.13)) @@ -8042,16 +8427,16 @@ snapshots: p-queue: 6.6.2 p-retry: 4.6.2 uuid: 9.0.1 - zod: 3.23.8 - zod-to-json-schema: 3.22.5(zod@3.23.8) + zod: 3.24.1 + zod-to-json-schema: 3.22.5(zod@3.24.1) '@langchain/openai@0.0.28(encoding@0.1.13)': dependencies: '@langchain/core': 0.1.61 js-tiktoken: 1.0.11 openai: 4.53.0(encoding@0.1.13) - zod: 3.23.8 - zod-to-json-schema: 3.22.5(zod@3.23.8) + zod: 3.24.1 + zod-to-json-schema: 3.22.5(zod@3.24.1) transitivePeerDependencies: - encoding @@ -8060,12 +8445,12 @@ snapshots: '@langchain/core': 0.1.61 js-tiktoken: 1.0.11 - '@mistralai/mistralai-gcp@1.3.5(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.23.8)': + '@mistralai/mistralai-gcp@1.3.5(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.24.1)': dependencies: google-auth-library: 9.14.2(encoding@0.1.13) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - zod: 3.23.8 + zod: 3.24.1 transitivePeerDependencies: - encoding - supports-color @@ -8084,7 +8469,33 @@ snapshots: dependencies: content-type: 1.0.5 raw-body: 3.0.0 - zod: 3.23.8 + zod: 3.24.1 + + '@next/env@15.1.6': {} + + '@next/swc-darwin-arm64@15.1.6': + optional: true + + '@next/swc-darwin-x64@15.1.6': + optional: true + + '@next/swc-linux-arm64-gnu@15.1.6': + optional: true + + '@next/swc-linux-arm64-musl@15.1.6': + optional: true + + '@next/swc-linux-x64-gnu@15.1.6': + optional: true + + '@next/swc-linux-x64-musl@15.1.6': + optional: true + + '@next/swc-win32-arm64-msvc@15.1.6': + optional: true + + '@next/swc-win32-x64-msvc@15.1.6': + optional: true '@opentelemetry/api-logs@0.52.1': dependencies: @@ -8826,6 +9237,12 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@swc/counter@0.1.3': {} + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + '@tootallnate/once@2.0.0': {} '@tsconfig/node10@1.0.11': @@ -8995,6 +9412,14 @@ snapshots: '@types/range-parser@1.2.7': {} + '@types/react-dom@19.0.3(@types/react@19.0.8)': + dependencies: + '@types/react': 19.0.8 + + '@types/react@19.0.8': + dependencies: + csstype: 3.1.3 + '@types/request@2.48.12': dependencies: '@types/caseless': 0.12.5 @@ -9301,6 +9726,10 @@ snapshots: esbuild: 0.24.0 load-tsconfig: 0.2.5 + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + bytes@3.1.2: {} cac@6.7.14: {} @@ -9373,6 +9802,8 @@ snapshots: cjs-module-lexer@1.2.3: {} + client-only@0.0.1: {} + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -9405,6 +9836,12 @@ snapshots: color-convert: 1.9.3 color-string: 1.9.1 + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + optional: true + colorette@2.0.20: {} colorspace@1.1.4: @@ -9513,6 +9950,8 @@ snapshots: crypt@0.0.2: {} + csstype@3.1.3: {} + data-uri-to-buffer@4.0.1: {} data-urls@5.0.0: @@ -9945,7 +10384,7 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 - file-type-checker@1.1.2: {} + file-type-checker@1.1.3: {} filelist@1.0.4: dependencies: @@ -10139,12 +10578,12 @@ snapshots: - encoding - supports-color - genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-rc.5)(@genkit-ai/core@1.0.0-rc.5): + genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-rc.10)(@genkit-ai/core@1.0.0-rc.10): dependencies: - '@genkit-ai/ai': 1.0.0-rc.5 - '@genkit-ai/core': 1.0.0-rc.5 + '@genkit-ai/ai': 1.0.0-rc.10 + '@genkit-ai/core': 1.0.0-rc.10 openai: 4.53.0(encoding@0.1.13) - zod: 3.23.8 + zod: 3.24.1 transitivePeerDependencies: - encoding @@ -11158,8 +11597,8 @@ snapshots: p-retry: 4.6.2 uuid: 9.0.1 yaml: 2.4.1 - zod: 3.23.8 - zod-to-json-schema: 3.22.5(zod@3.23.8) + zod: 3.24.1 + zod-to-json-schema: 3.22.5(zod@3.24.1) optionalDependencies: '@google-cloud/storage': 7.10.1(encoding@0.1.13) '@pinecone-database/pinecone': 2.2.0 @@ -11466,8 +11905,7 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.7: - optional: true + nanoid@3.3.7: {} napi-build-utils@1.0.2: optional: true @@ -11478,6 +11916,32 @@ snapshots: neo-async@2.6.2: {} + next@15.1.6(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@next/env': 15.1.6 + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.15 + busboy: 1.6.0 + caniuse-lite: 1.0.30001667 + postcss: 8.4.31 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + styled-jsx: 5.1.6(react@18.3.1) + optionalDependencies: + '@next/swc-darwin-arm64': 15.1.6 + '@next/swc-darwin-x64': 15.1.6 + '@next/swc-linux-arm64-gnu': 15.1.6 + '@next/swc-linux-arm64-musl': 15.1.6 + '@next/swc-linux-x64-gnu': 15.1.6 + '@next/swc-linux-x64-musl': 15.1.6 + '@next/swc-win32-arm64-msvc': 15.1.6 + '@next/swc-win32-x64-msvc': 15.1.6 + '@opentelemetry/api': 1.9.0 + sharp: 0.33.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + node-abi@3.71.0: dependencies: semver: 7.6.3 @@ -11713,8 +12177,6 @@ snapshots: postgres-date: 1.0.7 postgres-interval: 1.2.0 - picocolors@1.1.0: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -11741,6 +12203,12 @@ snapshots: tsx: 4.19.2 yaml: 2.6.1 + postcss@8.4.31: + dependencies: + nanoid: 3.3.7 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postcss@8.4.47: dependencies: nanoid: 3.3.7 @@ -12060,6 +12528,33 @@ snapshots: setprototypeof@1.2.0: {} + sharp@0.33.5: + dependencies: + color: 4.2.3 + detect-libc: 2.0.3 + semver: 7.6.3 + 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 + optional: true + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -12106,8 +12601,7 @@ snapshots: slash@3.0.0: {} - source-map-js@1.2.1: - optional: true + source-map-js@1.2.1: {} source-map-support@0.5.13: dependencies: @@ -12150,6 +12644,8 @@ snapshots: stream-shift@1.0.3: {} + streamsearch@1.1.0: {} + string-length@4.0.2: dependencies: char-regex: 1.0.2 @@ -12221,6 +12717,11 @@ snapshots: stubs@3.0.0: {} + styled-jsx@5.1.6(react@18.3.1): + dependencies: + client-only: 0.0.1 + react: 18.3.1 + sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.5 @@ -12404,6 +12905,8 @@ snapshots: tslib@2.6.2: {} + tslib@2.8.1: {} + tsup@8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1): dependencies: bundle-require: 5.0.0(esbuild@0.24.0) @@ -12571,7 +13074,7 @@ snapshots: dependencies: browserslist: 4.24.0 escalade: 3.2.0 - picocolors: 1.1.0 + picocolors: 1.1.1 uri-js@4.4.1: dependencies: @@ -12753,6 +13256,12 @@ snapshots: dependencies: zod: 3.23.8 + zod-to-json-schema@3.22.5(zod@3.24.1): + dependencies: + zod: 3.24.1 + zod@3.22.4: {} zod@3.23.8: {} + + zod@3.24.1: {} diff --git a/js/testapps/next/package.json b/js/testapps/next/package.json new file mode 100644 index 000000000..0a950e41b --- /dev/null +++ b/js/testapps/next/package.json @@ -0,0 +1,30 @@ +{ + "name": "nextjs-sample", + "type": "module", + "version": "0.0.1-dev.1", + "description": "Sample app to test @genkit-ai/nextjs", + "main": "lib/index.js", + "scripts": { + "vendor": "cd ../../plugins/next && pnpm pack --pack-destination ../../testapps/next && cd - && pnpm install ./genkit-ai-next*.tgz --filter nextjs-sample", + "start": "next start", + "build": "next build", + "dev": "next dev", + "build:clean": "rimraf ./lib", + "build:watch": "next dev" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@genkit-ai/googleai": "workspace:*", + "@genkit-ai/next": "workspace:*", + "genkit": "workspace:*", + "next": "^15.1.6", + "zod": "^3.24.1" + }, + "devDependencies": { + "@types/react": "19.0.8", + "rimraf": "^6.0.1", + "typescript": "^5.3.3" + } +} diff --git a/js/testapps/next/src/app/api/joke/route.ts b/js/testapps/next/src/app/api/joke/route.ts new file mode 100644 index 000000000..821eaefea --- /dev/null +++ b/js/testapps/next/src/app/api/joke/route.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ + +import { tellJoke } from '@/genkit/joke'; +import { appRoute } from '@genkit-ai/next'; + +export const POST = appRoute(tellJoke); diff --git a/js/testapps/next/src/app/globals.css b/js/testapps/next/src/app/globals.css new file mode 100644 index 000000000..472befff6 --- /dev/null +++ b/js/testapps/next/src/app/globals.css @@ -0,0 +1,18 @@ +:root { + --background: #ffffff; + --foreground: #171717; +} + +html, +body { + max-width: 100vw; + overflow-x: hidden; +} + +body { + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/js/testapps/next/src/app/layout.tsx b/js/testapps/next/src/app/layout.tsx new file mode 100644 index 000000000..a5c0a8e17 --- /dev/null +++ b/js/testapps/next/src/app/layout.tsx @@ -0,0 +1,32 @@ +import type { Metadata } from 'next'; +import { Geist, Geist_Mono } from 'next/font/google'; +import './globals.css'; + +const geistSans = Geist({ + variable: '--font-geist-sans', + subsets: ['latin'], +}); + +const geistMono = Geist_Mono({ + variable: '--font-geist-mono', + subsets: ['latin'], +}); + +export const metadata: Metadata = { + title: 'Genkit Next.js sample app', + description: 'Test app for the @genkit-ai/next plugin', +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/js/testapps/next/src/app/page.module.css b/js/testapps/next/src/app/page.module.css new file mode 100644 index 000000000..62889cafb --- /dev/null +++ b/js/testapps/next/src/app/page.module.css @@ -0,0 +1,5 @@ +.title { + display: flex; + gap: 10px; + margin-bottom: 10px; +} diff --git a/js/testapps/next/src/app/page.tsx b/js/testapps/next/src/app/page.tsx new file mode 100644 index 000000000..3c78868ca --- /dev/null +++ b/js/testapps/next/src/app/page.tsx @@ -0,0 +1,66 @@ +'use client'; + +import { tellJoke } from '@/genkit/joke'; +import { runFlow, streamFlow } from '@genkit-ai/next/client'; +import { useEffect, useRef, useState } from 'react'; +import styles from './page.module.css'; + +async function run(type: string, setResponse: (response: string) => void) { + setResponse('...'); + const resp = await runFlow({ + url: '/api/joke', + input: type === '' ? null : type, + }); + setResponse(resp); +} + +async function stream(type: string, setResponse: (response: string) => void) { + let accum = ''; + setResponse('...'); + const { stream, output } = streamFlow({ + url: '/api/joke', + input: type === '' ? null : type, + }); + for await (const chunk of stream) { + accum = accum + chunk; + setResponse(accum); + } + setResponse(await output); +} + +export default function Home() { + const inputRef = useRef(null); + const [response, setResponse] = useState('Pick a joke type'); + function focus() { + if (inputRef.current) { + inputRef.current.focus(); + } + } + useEffect(focus); + return ( + <> +
+ + + +
+
{response}
+ + ); +} diff --git a/js/testapps/next/src/genkit/index.ts b/js/testapps/next/src/genkit/index.ts new file mode 100644 index 000000000..709b89985 --- /dev/null +++ b/js/testapps/next/src/genkit/index.ts @@ -0,0 +1,23 @@ +/** + * 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. + */ + +import { gemini15Flash, googleAI } from '@genkit-ai/googleai'; +import { genkit } from 'genkit'; + +export const ai = genkit({ + plugins: [googleAI()], + model: gemini15Flash, +}); diff --git a/js/testapps/next/src/genkit/joke.ts b/js/testapps/next/src/genkit/joke.ts new file mode 100644 index 000000000..786c93145 --- /dev/null +++ b/js/testapps/next/src/genkit/joke.ts @@ -0,0 +1,36 @@ +/** + * 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. + */ + +import { z } from 'genkit'; +import { ai } from '.'; + +export const tellJoke = ai.defineFlow( + { + name: 'tellJoke', + inputSchema: z.string().nullable(), + outputSchema: z.string(), + streamSchema: z.string(), + }, + async (type: string | null, { sendChunk }) => { + const { response, stream } = ai.generateStream( + `Tell me a ${type || 'dad'} joke` + ); + for await (const chunk of stream) { + sendChunk(chunk.text); + } + return (await response).text; + } +); diff --git a/js/testapps/next/tsconfig.json b/js/testapps/next/tsconfig.json new file mode 100644 index 000000000..c3fb5b7e5 --- /dev/null +++ b/js/testapps/next/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "noImplicitReturns": true, + "noUnusedLocals": false, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017", + "skipLibCheck": true, + "esModuleInterop": true, + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "noEmit": true, + "incremental": true, + "isolatedModules": true, + "jsx": "preserve", + "plugins": [ + { + "name": "next" + } + ], + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, + "moduleResolution": "bundler", + "resolveJsonModule": true, + "module": "esnext" + }, + "compileOnSave": true, + "include": ["src", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} From a0463a2850ccc8779fe16fb77bd567fd8080f566 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Wed, 29 Jan 2025 22:56:37 -0500 Subject: [PATCH 391/562] fix(js/ai/prompt): fix prompt loading in node 20, make sure-sure that .prompt loading is lazy (#1700) --- .github/workflows/builder.yml | 4 +- js/ai/src/prompt.ts | 54 ++++++++++----------- js/core/src/action.ts | 35 +++++++------ js/core/src/async.ts | 11 +++++ js/genkit/tests/prompts/badSchemaRef.prompt | 9 ++++ js/genkit/tests/prompts_test.ts | 10 +++- 6 files changed, 77 insertions(+), 46 deletions(-) create mode 100644 js/genkit/tests/prompts/badSchemaRef.prompt diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml index af790a05f..5eb75618c 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/builder.yml @@ -27,10 +27,10 @@ jobs: steps: - uses: actions/checkout@v3 - uses: pnpm/action-setup@v3 - - name: Set up node v21 + - name: Set up node v20 uses: actions/setup-node@v4 with: - node-version: 21.x + node-version: 20.x cache: 'pnpm' - name: Install dependencies run: pnpm install diff --git a/js/ai/src/prompt.ts b/js/ai/src/prompt.ts index 5eb5cfc35..506a684c1 100644 --- a/js/ai/src/prompt.ts +++ b/js/ai/src/prompt.ts @@ -25,7 +25,7 @@ import { stripUndefinedProps, z, } from '@genkit-ai/core'; -import { LazyPromise } from '@genkit-ai/core/async'; +import { lazy } from '@genkit-ai/core/async'; import { logger } from '@genkit-ai/core/logging'; import { Registry } from '@genkit-ai/core/registry'; import { toJsonSchema } from '@genkit-ai/core/schema'; @@ -311,8 +311,8 @@ function definePromptAsync< }, }); }; - const rendererActionConfig = optionsPromise.then( - (options: PromptConfig) => { + const rendererActionConfig = lazy(() => + optionsPromise.then((options: PromptConfig) => { const metadata = promptMetadata(options); return { name: `${options.name}${options.variant ? `.${options.variant}` : ''}`, @@ -330,17 +330,21 @@ function definePromptAsync< ); }, } as ActionAsyncParams; - } + }) ); const rendererAction = defineActionAsync( registry, 'prompt', name, - rendererActionConfig + rendererActionConfig, + (action) => { + (action as PromptAction).__executablePrompt = + executablePrompt as never as ExecutablePrompt>; + } ) as Promise>; - const executablePromptActionConfig = optionsPromise.then( - (options: PromptConfig) => { + const executablePromptActionConfig = lazy(() => + optionsPromise.then((options: PromptConfig) => { const metadata = promptMetadata(options); return { name: `${options.name}${options.variant ? `.${options.variant}` : ''}`, @@ -359,14 +363,18 @@ function definePromptAsync< }); }, } as ActionAsyncParams; - } + }) ); - const executablePromptAction = defineActionAsync( + defineActionAsync( registry, 'executable-prompt', name, - executablePromptActionConfig + executablePromptActionConfig, + (action) => { + (action as ExecutablePromptAction).__executablePrompt = + executablePrompt as never as ExecutablePrompt>; + } ) as Promise>; const executablePrompt = wrapInExecutablePrompt( @@ -375,17 +383,6 @@ function definePromptAsync< rendererAction ); - executablePromptAction.then((action) => { - action.__executablePrompt = executablePrompt as never as ExecutablePrompt< - z.infer - >; - }); - rendererAction.then((action) => { - action.__executablePrompt = executablePrompt as never as ExecutablePrompt< - z.infer - >; - }); - return executablePrompt; } @@ -670,26 +667,27 @@ export function loadPromptFolder( recursive: true, }); for (const dirEnt of dirEnts) { + const parentPath = dirEnt.parentPath ?? dirEnt.path; if (dirEnt.isFile() && dirEnt.name.endsWith('.prompt')) { if (dirEnt.name.startsWith('_')) { const partialName = dirEnt.name.substring(1, dirEnt.name.length - 7); definePartial( registry, partialName, - readFileSync(join(dirEnt.path, dirEnt.name), { + readFileSync(join(parentPath, dirEnt.name), { encoding: 'utf8', }) ); logger.debug( - `Registered Dotprompt partial "${partialName}" from "${join(dirEnt.parentPath, dirEnt.name)}"` + `Registered Dotprompt partial "${partialName}" from "${join(parentPath, dirEnt.name)}"` ); } else { // If this prompt is in a subdirectory, we need to include that // in the namespace to prevent naming conflicts. let prefix = ''; let name = dirEnt.name; - if (promptsPath !== dirEnt.parentPath) { - prefix = dirEnt.parentPath.replace(`${promptsPath}/`, '') + '/'; + if (promptsPath !== parentPath) { + prefix = parentPath.replace(`${promptsPath}/`, '') + '/'; } loadPrompt(registry, promptsPath, name, prefix, ns); } @@ -736,7 +734,7 @@ function loadPrompt( // We use a lazy promise here because we only want prompt loaded when it's first used. // This is important because otherwise the loading may happen before the user has configured // all the schemas, etc., which will result in dotprompt.renderMetadata errors. - new LazyPromise(async (resolvePromptConfig) => { + lazy(async () => { const promptMetadata = await registry.dotprompt.renderMetadata(parsedPrompt); if (variant) { @@ -751,7 +749,7 @@ function loadPrompt( delete promptMetadata.input.schema.description; } - resolvePromptConfig({ + return { name: registryDefinitionKey(name, variant ?? undefined, ns), model: promptMetadata.model, config: promptMetadata.config, @@ -776,7 +774,7 @@ function loadPrompt( toolChoice: promptMetadata.raw?.['toolChoice'], returnToolRequests: promptMetadata.raw?.['returnToolRequests'], messages: parsedPrompt.template, - }); + }; }) ); } diff --git a/js/core/src/action.ts b/js/core/src/action.ts index b942c26d1..1bc7362a3 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -16,6 +16,7 @@ import { JSONSchema7 } from 'json-schema'; import * as z from 'zod'; +import { lazy } from './async.js'; import { ActionContext, getContext, runWithContext } from './context.js'; import { ActionType, Registry } from './registry.js'; import { parseSchema } from './schema.js'; @@ -446,24 +447,28 @@ export function defineActionAsync< pluginId: string; actionId: string; }, - config: PromiseLike> + config: PromiseLike>, + onInit?: (action: Action) => void ): PromiseLike> { const actionName = typeof name === 'string' ? name : `${name.pluginId}/${name.actionId}`; - const actionPromise = config.then((resolvedConfig) => { - const act = action( - registry, - resolvedConfig, - async (i: I, options): Promise> => { - await registry.initializeAllPlugins(); - return await runInActionRuntimeContext(registry, () => - resolvedConfig.fn(i, options) - ); - } - ); - act.__action.actionType = actionType; - return act; - }); + const actionPromise = lazy(() => + config.then((resolvedConfig) => { + const act = action( + registry, + resolvedConfig, + async (i: I, options): Promise> => { + await registry.initializeAllPlugins(); + return await runInActionRuntimeContext(registry, () => + resolvedConfig.fn(i, options) + ); + } + ); + act.__action.actionType = actionType; + onInit?.(act); + return act; + }) + ); registry.registerActionAsync(actionType, actionName, actionPromise); return actionPromise; } diff --git a/js/core/src/async.ts b/js/core/src/async.ts index 2a4d9a05f..35d93ca61 100644 --- a/js/core/src/async.ts +++ b/js/core/src/async.ts @@ -113,3 +113,14 @@ export class LazyPromise implements PromiseLike { return this.promise.then(onfulfilled, onrejected); } } + +/** Lazily call the provided function to resolve the LazyPromise. */ +export function lazy(fn: () => T | PromiseLike): PromiseLike { + return new LazyPromise((resolve, reject) => { + try { + resolve(fn()); + } catch (e) { + reject(e); + } + }); +} diff --git a/js/genkit/tests/prompts/badSchemaRef.prompt b/js/genkit/tests/prompts/badSchemaRef.prompt new file mode 100644 index 000000000..84956e472 --- /dev/null +++ b/js/genkit/tests/prompts/badSchemaRef.prompt @@ -0,0 +1,9 @@ +--- +model: googleai/gemini-1.5-flash +input: + schema: badSchemaRef1 +output: + schema: badSchemaRef2 +--- + +doesn't matter \ No newline at end of file diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index f72d0e2aa..5075d8700 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -924,7 +924,7 @@ describe('prompt', () => { }); }); - it.only('loads from from the sub folder', async () => { + it('loads from from the sub folder', async () => { const testPrompt = ai.prompt('sub/test'); // see tests/prompts/sub folder const { text } = await testPrompt(); @@ -1043,6 +1043,14 @@ describe('prompt', () => { ); }); + it('lazily resolved schema refs', async () => { + const prompt = ai.prompt('badSchemaRef'); + + await assert.rejects(prompt.render({ foo: 'bar' }), (e: Error) => + e.message.includes("NOT_FOUND: Schema 'badSchemaRef1' not found") + ); + }); + it('loads a varaint from from the folder', async () => { const testPrompt = ai.prompt('test', { variant: 'variant' }); // see tests/prompts folder From bca77599f12999384042e94186de770c749ef8fd Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 30 Jan 2025 08:35:58 -0500 Subject: [PATCH 392/562] chore(js): loosen up plugin deps on core packages (from exact match to ^) (#1696) --- js/plugins/checks/package.json | 4 +-- js/plugins/chroma/package.json | 2 +- js/plugins/dev-local-vectorstore/package.json | 2 +- js/plugins/evaluators/package.json | 2 +- js/plugins/express/package.json | 2 +- js/plugins/firebase/package.json | 6 ++-- js/plugins/google-cloud/package.json | 2 +- js/plugins/googleai/package.json | 2 +- js/plugins/langchain/package.json | 2 +- js/plugins/mcp/package.json | 4 +-- js/plugins/ollama/package.json | 2 +- js/plugins/pinecone/package.json | 2 +- js/plugins/vertexai/package.json | 2 +- js/pnpm-lock.yaml | 34 +++++++++---------- 14 files changed, 34 insertions(+), 34 deletions(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index a0d999b8c..5eb51e134 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -30,12 +30,12 @@ "author": "genkit", "license": "Apache-2.0", "dependencies": { - "@genkit-ai/ai": "workspace:*", + "@genkit-ai/ai": "workspace:^", "@googleapis/checks": "^4.0.2", "google-auth-library": "^9.6.3" }, "peerDependencies": { - "genkit": "workspace:*" + "genkit": "workspace:^" }, "devDependencies": { "@types/node": "^20.11.16", diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index 8954ea23a..9b511f681 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -34,7 +34,7 @@ "chromadb": "1.8.1" }, "peerDependencies": { - "genkit": "workspace:*" + "genkit": "workspace:^" }, "devDependencies": { "@types/node": "^20.11.16", diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index a8cf199cf..eb560fced 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -31,7 +31,7 @@ "ts-md5": "^1.3.1" }, "peerDependencies": { - "genkit": "workspace:*" + "genkit": "workspace:^" }, "devDependencies": { "@types/node": "^20.11.16", diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index d6a611bff..822e4f053 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -36,7 +36,7 @@ "dotprompt": "^1.0.0-dev.3 || ^1" }, "peerDependencies": { - "genkit": "workspace:*" + "genkit": "workspace:^" }, "devDependencies": { "@types/node": "^20.11.16", diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index 6792e0a61..92a6ff735 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -32,7 +32,7 @@ "body-parser": "^1.20.3" }, "peerDependencies": { - "genkit": "workspace:*", + "genkit": "workspace:^", "express": "^4.21.1" }, "devDependencies": { diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 13f06f3fe..93a772f25 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -31,8 +31,8 @@ "author": "genkit", "license": "Apache-2.0", "dependencies": { - "@genkit-ai/google-cloud": "workspace:*", - "@genkit-ai/express": "workspace:*", + "@genkit-ai/google-cloud": "workspace:^", + "@genkit-ai/express": "workspace:^", "express": "^4.21.0", "google-auth-library": "^9.6.3" }, @@ -40,7 +40,7 @@ "@google-cloud/firestore": "^7.6.0", "firebase-admin": ">=12.2", "firebase-functions": ">=4.8", - "genkit": "workspace:*" + "genkit": "workspace:^" }, "devDependencies": { "@types/node": "^20.11.16", diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index b772a7254..76cd4d7e0 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -50,7 +50,7 @@ "winston": "^3.12.0" }, "peerDependencies": { - "genkit": "workspace:*" + "genkit": "workspace:^" }, "devDependencies": { "@jest/globals": "^29.7.0", diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index e06e849b6..e1068b445 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -36,7 +36,7 @@ "node-fetch": "^3.3.2" }, "peerDependencies": { - "genkit": "workspace:*" + "genkit": "workspace:^" }, "devDependencies": { "@types/node": "^20.11.16", diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index 77b2a4b91..a8ae35311 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -31,7 +31,7 @@ "@opentelemetry/api": "^1.9.0" }, "peerDependencies": { - "genkit": "workspace:*", + "genkit": "workspace:^", "langchain": "^0.1.36" }, "devDependencies": { diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index 2df8d98dc..2c1846478 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -38,8 +38,8 @@ "author": "genkit", "license": "Apache-2.0", "peerDependencies": { - "genkit": "workspace:*", - "@genkit-ai/core": "workspace:*" + "genkit": "workspace:^", + "@genkit-ai/core": "workspace:^" }, "devDependencies": { "@jest/globals": "^29.7.0", diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index f556160b9..736d99597 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -29,7 +29,7 @@ "author": "genkit", "license": "Apache-2.0", "peerDependencies": { - "genkit": "workspace:*" + "genkit": "workspace:^" }, "dependencies": { "ollama": "^0.5.9" diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index 0faeffeee..1da3c9f24 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -34,7 +34,7 @@ "ts-md5": "^1.3.1" }, "peerDependencies": { - "genkit": "workspace:*" + "genkit": "workspace:^" }, "devDependencies": { "@types/node": "^20.11.16", diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 412d4055b..4f55f0e48 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -46,7 +46,7 @@ "openai": "^4.52.7" }, "peerDependencies": { - "genkit": "workspace:*" + "genkit": "workspace:^" }, "optionalDependencies": { "@google-cloud/bigquery": "^7.8.0", diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 32b71ea51..98f8ef086 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -238,13 +238,13 @@ importers: plugins/checks: dependencies: '@genkit-ai/ai': - specifier: workspace:* + specifier: workspace:^ version: link:../../ai '@googleapis/checks': specifier: ^4.0.2 version: 4.0.2(encoding@0.1.13) genkit: - specifier: workspace:* + specifier: workspace:^ version: link:../../genkit google-auth-library: specifier: ^9.6.3 @@ -275,7 +275,7 @@ importers: specifier: 1.8.1 version: 1.8.1(encoding@0.1.13)(openai@4.53.0(encoding@0.1.13)) genkit: - specifier: workspace:* + specifier: workspace:^ version: link:../../genkit ts-md5: specifier: ^1.3.1 @@ -306,7 +306,7 @@ importers: specifier: ^1.1.0 version: 1.1.0 genkit: - specifier: workspace:* + specifier: workspace:^ version: link:../../genkit ts-md5: specifier: ^1.3.1 @@ -340,7 +340,7 @@ importers: specifier: ^1.0.0-dev.3 || ^1 version: 1.0.0-dev.3 genkit: - specifier: workspace:* + specifier: workspace:^ version: link:../../genkit node-fetch: specifier: ^3.3.2 @@ -380,7 +380,7 @@ importers: specifier: ^4.21.1 version: 4.21.1 genkit: - specifier: workspace:* + specifier: workspace:^ version: link:../../genkit devDependencies: '@types/body-parser': @@ -417,10 +417,10 @@ importers: plugins/firebase: dependencies: '@genkit-ai/express': - specifier: workspace:* + specifier: workspace:^ version: link:../express '@genkit-ai/google-cloud': - specifier: workspace:* + specifier: workspace:^ version: link:../google-cloud '@google-cloud/firestore': specifier: ^7.6.0 @@ -435,7 +435,7 @@ importers: specifier: '>=4.8' version: 4.8.1(encoding@0.1.13)(firebase-admin@12.3.1(encoding@0.1.13)) genkit: - specifier: workspace:* + specifier: workspace:^ version: link:../../genkit google-auth-library: specifier: ^9.6.3 @@ -523,7 +523,7 @@ importers: specifier: ^1.25.0 version: 1.25.1(@opentelemetry/api@1.9.0) genkit: - specifier: workspace:* + specifier: workspace:^ version: link:../../genkit google-auth-library: specifier: ^9.6.3 @@ -569,7 +569,7 @@ importers: specifier: ^0.21.0 version: 0.21.0 genkit: - specifier: workspace:* + specifier: workspace:^ version: link:../../genkit google-auth-library: specifier: ^9.6.3 @@ -609,7 +609,7 @@ importers: specifier: ^1.9.0 version: 1.9.0 genkit: - specifier: workspace:* + specifier: workspace:^ version: link:../../genkit langchain: specifier: ^0.1.36 @@ -637,13 +637,13 @@ importers: plugins/mcp: dependencies: '@genkit-ai/core': - specifier: workspace:* + specifier: workspace:^ version: link:../../core '@modelcontextprotocol/sdk': specifier: 1.0.0 version: 1.0.0 genkit: - specifier: workspace:* + specifier: workspace:^ version: link:../../genkit devDependencies: '@jest/globals': @@ -716,7 +716,7 @@ importers: plugins/ollama: dependencies: genkit: - specifier: workspace:* + specifier: workspace:^ version: link:../../genkit ollama: specifier: ^0.5.9 @@ -747,7 +747,7 @@ importers: specifier: ^2.0.1 version: 2.2.0 genkit: - specifier: workspace:* + specifier: workspace:^ version: link:../../genkit ts-md5: specifier: ^1.3.1 @@ -790,7 +790,7 @@ importers: specifier: ^1.3.5 version: 1.3.5(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.24.1) genkit: - specifier: workspace:* + specifier: workspace:^ version: link:../../genkit google-auth-library: specifier: ^9.14.2 From f8a8310b709263b55142541bb0b7fe461d93c101 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:16:37 +0000 Subject: [PATCH 393/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.11 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 772d28ac2..888511a5b 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 7931a321e225f40b35f2e7104ce3691ad884b631 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:16:41 +0000 Subject: [PATCH 394/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.11 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 0befb6cd9..8bfef5fc5 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From 114fc31995cfd9fcf01bd2366a4e8039867707c2 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:16:43 +0000 Subject: [PATCH 395/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.11 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index f35df5713..6d6593200 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From fe5bfbb8e07b98af88f727cbc1b38c95dfcfe107 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:17:57 +0000 Subject: [PATCH 396/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.11 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 605c9335e..066093f23 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From 48b236b8fbbdc92e8f118522f2df36f1200407ca Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:17:59 +0000 Subject: [PATCH 397/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.11 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index 784cf78dc..43397fae3 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From 930218e164ef3edbe98bcfe3aa661079388f30a3 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:01 +0000 Subject: [PATCH 398/562] chore: bump genkit version to genkit@1.0.0-rc.11 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index c2d999cfb..ed5bd7934 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From 703e2f776bd713f59fe5c761b47ea06fbadd511c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:04 +0000 Subject: [PATCH 399/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.11 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index 9b511f681..9ab2a3c64 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From fb4336df9b97801302a530330ec9a2acede9b8a2 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:07 +0000 Subject: [PATCH 400/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.11 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index eb560fced..90fa14b88 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From 4d98a02317c7aad47f6414d5220b52e52f4b84c6 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:09 +0000 Subject: [PATCH 401/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.11 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 822e4f053..2815c7e69 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From 20d24631e5ae00b93c7284c6169d504a66f1fb56 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:11 +0000 Subject: [PATCH 402/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.11 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 93a772f25..7191bed78 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From c71f1b4b514ecbb7a5e6d68322c762e4c88df0df Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:14 +0000 Subject: [PATCH 403/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.11 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index 76cd4d7e0..7e673e4c1 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From 3ac1799c2cd23f3d9f1902cb5bb400a3fa43be38 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:16 +0000 Subject: [PATCH 404/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.11 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index e1068b445..822274f84 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From e4eabc841012067bcb1aa9befbf77d12f7644987 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:19 +0000 Subject: [PATCH 405/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.11 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index a8ae35311..ad79f6d93 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From b27f114e49af127c1c54d6f7478da1c6c80b3e84 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:21 +0000 Subject: [PATCH 406/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.11 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index 736d99597..1c4e60396 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From 142ca654a77c2936a95c6daacf81a84edb3f24be Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:24 +0000 Subject: [PATCH 407/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.11 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index 1da3c9f24..1c4524a60 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From 8c86624de62a9d013bd95a7d1a5844a26773f737 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:26 +0000 Subject: [PATCH 408/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.11 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 4f55f0e48..2d57475bf 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From 2346be36c64dec8d1c18401e6d6ddbbf17df5274 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:29 +0000 Subject: [PATCH 409/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.11 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index 5eb51e134..e5fb5d6b1 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From 8623076ecefe1d2b7c805dcc490f26c95f42536e Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:31 +0000 Subject: [PATCH 410/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.11 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index 2c1846478..f6cbcdcf0 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From fc708c9afdaa2c6029f75cc4a601417fa4d51235 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Thu, 30 Jan 2025 14:18:33 +0000 Subject: [PATCH 411/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.11 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index 92a6ff735..d4984ebf1 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.10", + "version": "1.0.0-rc.11", "type": "commonjs", "scripts": { "check": "tsc", From 0ea2de50a3e4112a720664e29d626e7170cc2e4a Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 30 Jan 2025 10:26:08 -0500 Subject: [PATCH 412/562] chore(tests): remove list actions test (too fragile) --- tests/reflection_api_tests.yaml | 862 -------------------------------- 1 file changed, 862 deletions(-) diff --git a/tests/reflection_api_tests.yaml b/tests/reflection_api_tests.yaml index a33309a92..9ef520dc2 100644 --- a/tests/reflection_api_tests.yaml +++ b/tests/reflection_api_tests.yaml @@ -18,865 +18,3 @@ tests: role: model content: - text: '{"messages":[{"content":[{"text":"hello"}],"role":"user"}]}' - - - path: /api/actions - body: - /model/customReflector: - key: /model/customReflector - name: customReflector - metadata: { model: { label: 'customReflector' } } - inputSchema: - { - 'type': 'object', - 'properties': - { - 'messages': - { - 'type': 'array', - 'items': - { - 'type': 'object', - 'properties': - { - 'role': - { - 'type': 'string', - 'enum': ['system', 'user', 'model', 'tool'], - }, - 'content': - { - 'type': 'array', - 'items': - { - 'anyOf': - [ - { - 'type': 'object', - 'properties': - { - 'text': { 'type': 'string' }, - 'media': { 'not': {} }, - 'toolRequest': { 'not': {} }, - 'toolResponse': { 'not': {} }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': {}, - }, - }, - 'required': ['text'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': - { - 'type': 'object', - 'properties': - { - 'contentType': - { 'type': 'string' }, - 'url': - { 'type': 'string' }, - }, - 'required': ['url'], - 'additionalProperties': true, - }, - 'toolRequest': { 'not': {} }, - 'toolResponse': { 'not': {} }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': {}, - }, - }, - 'required': ['media'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': { 'not': {} }, - 'toolRequest': - { - 'type': 'object', - 'properties': - { - 'ref': - { 'type': 'string' }, - 'name': - { 'type': 'string' }, - 'input': {}, - }, - 'required': ['name'], - 'additionalProperties': true, - }, - 'toolResponse': { 'not': {} }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': {}, - }, - }, - 'required': ['toolRequest'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': { 'not': {} }, - 'toolRequest': { 'not': {} }, - 'toolResponse': - { - 'type': 'object', - 'properties': - { - 'ref': - { 'type': 'string' }, - 'name': - { 'type': 'string' }, - 'output': {}, - }, - 'required': ['name'], - 'additionalProperties': true, - }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': {}, - }, - }, - 'required': ['toolResponse'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': { 'not': {} }, - 'toolRequest': { 'not': {} }, - 'toolResponse': { 'not': {} }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': {}, - }, - }, - 'additionalProperties': true, - }, - ], - }, - }, - 'metadata': - { 'type': 'object', 'additionalProperties': {} }, - }, - 'required': ['role', 'content'], - 'additionalProperties': true, - }, - }, - 'config': {}, - 'tools': - { - 'type': 'array', - 'items': - { - 'type': 'object', - 'properties': - { - 'name': { 'type': 'string' }, - 'description': { 'type': 'string' }, - 'inputSchema': - { - 'type': 'object', - 'additionalProperties': {}, - 'description': 'Valid JSON Schema representing the input of the tool.', - }, - 'outputSchema': - { - 'type': 'object', - 'additionalProperties': {}, - 'description': 'Valid JSON Schema describing the output of the tool.', - }, - }, - 'required': ['name', 'description', 'inputSchema'], - 'additionalProperties': true, - }, - }, - 'output': - { - 'type': 'object', - 'properties': - { - 'format': - { - 'type': 'string', - 'enum': ['json', 'text', 'media'], - }, - 'schema': - { 'type': 'object', 'additionalProperties': {} }, - }, - 'additionalProperties': true, - }, - 'context': - { - 'type': 'array', - 'items': - { - 'type': 'object', - 'properties': - { - 'content': - { - 'type': 'array', - 'items': - { - 'anyOf': - [ - { - 'type': 'object', - 'properties': - { - 'text': { 'type': 'string' }, - 'media': { 'not': {} }, - }, - 'required': ['text'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': - { - 'type': 'object', - 'properties': - { - 'contentType': - { 'type': 'string' }, - 'url': - { 'type': 'string' }, - }, - 'required': ['url'], - 'additionalProperties': true, - }, - }, - 'required': ['media'], - 'additionalProperties': true, - }, - ], - }, - }, - 'metadata': - { 'type': 'object', 'additionalProperties': {} }, - }, - 'required': ['content'], - 'additionalProperties': true, - }, - }, - 'candidates': { 'type': 'number' }, - }, - 'required': ['messages'], - 'additionalProperties': true, - } - outputSchema: - { - 'type': 'object', - 'properties': - { - 'candidates': - { - 'type': 'array', - 'items': - { - 'type': 'object', - 'properties': - { - 'index': { 'type': 'number' }, - 'message': - { - 'type': 'object', - 'properties': - { - 'role': - { - 'type': 'string', - 'enum': - ['system', 'user', 'model', 'tool'], - }, - 'content': - { - 'type': 'array', - 'items': - { - 'anyOf': - [ - { - 'type': 'object', - 'properties': - { - 'text': - { 'type': 'string' }, - 'media': { 'not': {} }, - 'toolRequest': - { 'not': {} }, - 'toolResponse': - { 'not': {} }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': - {}, - }, - }, - 'required': ['text'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': - { - 'type': 'object', - 'properties': - { - 'contentType': - { - 'type': 'string', - }, - 'url': - { - 'type': 'string', - }, - }, - 'required': ['url'], - 'additionalProperties': true, - }, - 'toolRequest': - { 'not': {} }, - 'toolResponse': - { 'not': {} }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': - {}, - }, - }, - 'required': ['media'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': { 'not': {} }, - 'toolRequest': - { - 'type': 'object', - 'properties': - { - 'ref': - { - 'type': 'string', - }, - 'name': - { - 'type': 'string', - }, - 'input': {}, - }, - 'required': ['name'], - 'additionalProperties': true, - }, - 'toolResponse': - { 'not': {} }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': - {}, - }, - }, - 'required': ['toolRequest'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': { 'not': {} }, - 'toolRequest': - { 'not': {} }, - 'toolResponse': - { - 'type': 'object', - 'properties': - { - 'ref': - { - 'type': 'string', - }, - 'name': - { - 'type': 'string', - }, - 'output': {}, - }, - 'required': ['name'], - 'additionalProperties': true, - }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': - {}, - }, - }, - 'required': ['toolResponse'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': { 'not': {} }, - 'toolRequest': - { 'not': {} }, - 'toolResponse': - { 'not': {} }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': - {}, - }, - }, - 'additionalProperties': true, - }, - ], - }, - }, - 'metadata': - { - 'type': 'object', - 'additionalProperties': {}, - }, - }, - 'required': ['role', 'content'], - 'additionalProperties': true, - }, - 'usage': - { - 'type': 'object', - 'properties': - { - 'inputTokens': { 'type': 'number' }, - 'outputTokens': { 'type': 'number' }, - 'totalTokens': { 'type': 'number' }, - 'inputCharacters': { 'type': 'number' }, - 'outputCharacters': { 'type': 'number' }, - 'inputImages': { 'type': 'number' }, - 'outputImages': { 'type': 'number' }, - 'inputVideos': { 'type': 'number' }, - 'outputVideos': { 'type': 'number' }, - 'inputAudioFiles': { 'type': 'number' }, - 'outputAudioFiles': { 'type': 'number' }, - 'custom': - { - 'type': 'object', - 'additionalProperties': - { 'type': 'number' }, - }, - }, - 'additionalProperties': true, - }, - 'finishReason': - { - 'type': 'string', - 'enum': - [ - 'stop', - 'length', - 'blocked', - 'other', - 'unknown', - ], - }, - 'finishMessage': { 'type': 'string' }, - 'custom': {}, - }, - 'required': ['index', 'message', 'finishReason'], - 'additionalProperties': true, - }, - }, - 'latencyMs': { 'type': 'number' }, - 'usage': - { - 'type': 'object', - 'properties': - { - 'inputTokens': { 'type': 'number' }, - 'outputTokens': { 'type': 'number' }, - 'totalTokens': { 'type': 'number' }, - 'inputCharacters': { 'type': 'number' }, - 'outputCharacters': { 'type': 'number' }, - 'inputImages': { 'type': 'number' }, - 'outputImages': { 'type': 'number' }, - 'inputVideos': { 'type': 'number' }, - 'outputVideos': { 'type': 'number' }, - 'inputAudioFiles': { 'type': 'number' }, - 'outputAudioFiles': { 'type': 'number' }, - 'custom': - { - 'type': 'object', - 'additionalProperties': { 'type': 'number' }, - }, - }, - 'additionalProperties': true, - }, - 'custom': {}, - 'request': - { - 'type': 'object', - 'properties': - { - 'messages': - { - 'type': 'array', - 'items': - { - 'type': 'object', - 'properties': - { - 'role': - { - 'type': 'string', - 'enum': - ['system', 'user', 'model', 'tool'], - }, - 'content': - { - 'type': 'array', - 'items': - { - 'anyOf': - [ - { - 'type': 'object', - 'properties': - { - 'text': - { 'type': 'string' }, - 'media': { 'not': {} }, - 'toolRequest': - { 'not': {} }, - 'toolResponse': - { 'not': {} }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': - {}, - }, - }, - 'required': ['text'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': - { - 'type': 'object', - 'properties': - { - 'contentType': - { - 'type': 'string', - }, - 'url': - { - 'type': 'string', - }, - }, - 'required': ['url'], - 'additionalProperties': true, - }, - 'toolRequest': - { 'not': {} }, - 'toolResponse': - { 'not': {} }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': - {}, - }, - }, - 'required': ['media'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': { 'not': {} }, - 'toolRequest': - { - 'type': 'object', - 'properties': - { - 'ref': - { - 'type': 'string', - }, - 'name': - { - 'type': 'string', - }, - 'input': {}, - }, - 'required': ['name'], - 'additionalProperties': true, - }, - 'toolResponse': - { 'not': {} }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': - {}, - }, - }, - 'required': ['toolRequest'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': { 'not': {} }, - 'toolRequest': - { 'not': {} }, - 'toolResponse': - { - 'type': 'object', - 'properties': - { - 'ref': - { - 'type': 'string', - }, - 'name': - { - 'type': 'string', - }, - 'output': {}, - }, - 'required': ['name'], - 'additionalProperties': true, - }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': - {}, - }, - }, - 'required': ['toolResponse'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': { 'not': {} }, - 'toolRequest': - { 'not': {} }, - 'toolResponse': - { 'not': {} }, - 'data': {}, - 'metadata': - { - 'type': 'object', - 'additionalProperties': - {}, - }, - }, - 'additionalProperties': true, - }, - ], - }, - }, - 'metadata': - { - 'type': 'object', - 'additionalProperties': {}, - }, - }, - 'required': ['role', 'content'], - 'additionalProperties': true, - }, - }, - 'config': {}, - 'tools': - { - 'type': 'array', - 'items': - { - 'type': 'object', - 'properties': - { - 'name': { 'type': 'string' }, - 'description': { 'type': 'string' }, - 'inputSchema': - { - 'type': 'object', - 'additionalProperties': {}, - 'description': 'Valid JSON Schema representing the input of the tool.', - }, - 'outputSchema': - { - 'type': 'object', - 'additionalProperties': {}, - 'description': 'Valid JSON Schema describing the output of the tool.', - }, - }, - 'required': - ['name', 'description', 'inputSchema'], - 'additionalProperties': true, - }, - }, - 'output': - { - 'type': 'object', - 'properties': - { - 'format': - { - 'type': 'string', - 'enum': ['json', 'text', 'media'], - }, - 'schema': - { - 'type': 'object', - 'additionalProperties': {}, - }, - }, - 'additionalProperties': true, - }, - 'context': - { - 'type': 'array', - 'items': - { - 'type': 'object', - 'properties': - { - 'content': - { - 'type': 'array', - 'items': - { - 'anyOf': - [ - { - 'type': 'object', - 'properties': - { - 'text': - { 'type': 'string' }, - 'media': { 'not': {} }, - }, - 'required': ['text'], - 'additionalProperties': true, - }, - { - 'type': 'object', - 'properties': - { - 'text': { 'not': {} }, - 'media': - { - 'type': 'object', - 'properties': - { - 'contentType': - { - 'type': 'string', - }, - 'url': - { - 'type': 'string', - }, - }, - 'required': ['url'], - 'additionalProperties': true, - }, - }, - 'required': ['media'], - 'additionalProperties': true, - }, - ], - }, - }, - 'metadata': - { - 'type': 'object', - 'additionalProperties': {}, - }, - }, - 'required': ['content'], - 'additionalProperties': true, - }, - }, - 'candidates': { 'type': 'number' }, - }, - 'required': ['messages'], - 'additionalProperties': true, - }, - }, - 'required': ['candidates'], - 'additionalProperties': true, - } - - /flow/testFlow: - key: /flow/testFlow - name: testFlow - - /flow/streamy: - key: /flow/streamy - name: streamy From bcfa4d8a0645698b48da4ecf0b334d619989d262 Mon Sep 17 00:00:00 2001 From: Yesudeep Mangalapilly Date: Thu, 30 Jan 2025 08:03:35 -0800 Subject: [PATCH 413/562] feat: add GitHub actions for Python and pre-commit checks for go #1704 (#1711) Changelog: - [ ] Adds GitHub actions to check the PR for Python code changes See: https://docs.astral.sh/uv/guides/integration/github/ - [ ] Adds pre-commit checks so that go tests pass per commit. - [ ] Configure prettier to ignore .mypy_cache/ - [ ] Fixes formatting for some engdoc - [ ] Updates the py/bin/setup script to work in CI environment --- .github/workflows/python.yml | 47 +++++++++++++++++++++++++++++++++ .prettierignore | 9 ++++--- py/bin/setup | 38 +++++++++++++++++++++++--- py/captainhook.json | 6 +++++ py/engdoc/contributing/index.md | 2 +- py/engdoc/index.md | 4 +-- 6 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/python.yml diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml new file mode 100644 index 000000000..193606928 --- /dev/null +++ b/.github/workflows/python.yml @@ -0,0 +1,47 @@ +name: Python Checks + +on: + pull_request: + paths: + - 'py/**' + +jobs: + python-checks: + runs-on: ubuntu-latest + env: + PATH: ${{ github.workspace }}/go/bin:${{ github.workspace }}/.cargo/bin:${{ github.workspace }}/.local/share/pnpm:${{ github.workspace }}/.local/bin:${{ env.PATH }} + strategy: + matrix: + python-version: + - "3.12" + - "3.13" + defaults: + run: + working-directory: py + + steps: + - uses: actions/checkout@v4 + + - name: Pre-requisites + run: bin/setup -a ci + + - name: Install uv and setup Python version + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + python-version: ${{ matrix.python-version }} + + - name: Format check + run: uv run ruff format --check . + + - name: Lint with ruff + run: uv run ruff check . + + - name: Run tests + run: uv run pytest . + + - name: Build documentation + run: uv run mkdocs build --strict + + - name: Build distributions + run: ./bin/build_dists diff --git a/.prettierignore b/.prettierignore index 86e82a2ee..fedcb43fe 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,9 +1,10 @@ -go/ -public/ .github/ +.mypy_cache/* .prettierrc.yaml -pnpm-lock.yaml -docs/* docs-go/* +docs/* genkit-tools/genkit-schema.json +go/ +pnpm-lock.yaml +public/ py/* diff --git a/py/bin/setup b/py/bin/setup index f264ed8cc..7cf5efe14 100755 --- a/py/bin/setup +++ b/py/bin/setup @@ -50,6 +50,33 @@ fi OS_NAME=$(uname) +# Updates your shell profile to include a path. +function genkit::update_path() { + local new_path="$1" + + # Remove trailing slash if present. + new_path="${new_path%/}" + + if [ ! -d "$new_path" ]; then + echo "Error: Directory $new_path does not exist" + return 1 + fi + + # Check if path is already in PATH + if [[ ":$PATH:" != *":$new_path:"* ]]; then + if [ -n "${ZSH_VERSION:-}" ]; then + echo "export PATH=\"$new_path:\$PATH\"" >>"$HOME/.zshrc" + else + echo "export PATH=\"$new_path:\$PATH\"" >>"$HOME/.bashrc" + fi + + export PATH="$new_path:$PATH" + echo "Path $new_path added successfully" + else + echo "Path $new_path already exists in PATH" + fi +} + # Install all the required tools common to all audiences. function genkit::install_prerequisites() { if [[ ${OS_NAME} == "Darwin" && -x "$(command -v brew)" ]]; then @@ -88,7 +115,12 @@ function genkit::install_prerequisites() { curl -LsSf https://astral.sh/uv/install.sh | sh # Install pnpm for JavaScript package management. - curl -fsSL https://get.pnpm.io/install.sh | env PNPM_VERSION=10.0.0 sh - + # See: https://github.com/pnpm/pnpm/issues/6217 + curl -fsSL https://get.pnpm.io/install.sh | env ENV="$HOME/.bashrc" SHELL="$(which bash)" PNPM_VERSION=10.0.0 bash - + + genkit::update_path "$HOME/.cargo/bin" + genkit::update_path "$HOME/.local/share/pnpm" + genkit::update_path "$HOME/go/bin" } function genkit::install_google_cloud_sdk() { @@ -107,8 +139,8 @@ function genkit::install_google_cloud_sdk() { function genkit::install_ci_packages() { genkit::install_prerequisites genkit::install_python_cli_tools + genkit::install_go_cli_tools genkit::install_docs_cli_tools - genkit::install_pnpm_cli_tools } # Install all the required tools for engineering. @@ -132,7 +164,7 @@ function genkit::install_go_cli_tools() { # Install all the required tools that have been written in Rust. We're assuming # that the user has already installed rust and cargo. function genkit::install_cargo_cli_tools() { - "$HOME/.cargo/bin/cargo" install --locked \ + cargo install --locked \ convco \ fd-find \ ripgrep \ diff --git a/py/captainhook.json b/py/captainhook.json index 4bce26572..51547e326 100644 --- a/py/captainhook.json +++ b/py/captainhook.json @@ -47,6 +47,9 @@ }, { "run": "py/bin/build_dists" + }, + { + "run": "go test go/..." } ] }, @@ -82,6 +85,9 @@ { "run": "py/bin/build_dists" }, + { + "run": "go test go/..." + }, { "run": "py/.hooks/commit-message-format-pre-push" } diff --git a/py/engdoc/contributing/index.md b/py/engdoc/contributing/index.md index 222ff5859..5bcc39c64 100644 --- a/py/engdoc/contributing/index.md +++ b/py/engdoc/contributing/index.md @@ -185,5 +185,5 @@ py/bin/setup The following is the equivalent used for CI/CD systems. ```bash -py/bin/setup -e ci +py/bin/setup -a ci ``` diff --git a/py/engdoc/index.md b/py/engdoc/index.md index cf7c4bdb7..9e9c48ead 100644 --- a/py/engdoc/index.md +++ b/py/engdoc/index.md @@ -388,9 +388,9 @@ capabilities in code: === "Python" -```python + ```python -``` + ``` === "JavaScript" From 634ca57c27d84dc9cd79ad2f641667562eb946b6 Mon Sep 17 00:00:00 2001 From: Anthony Barone Date: Thu, 30 Jan 2025 12:05:49 -0500 Subject: [PATCH 414/562] chore: include package-lock files for samples (#1717) --- .gitignore | 5 +- samples/chatbot/package-lock.json | 354 + samples/js-angular/package-lock.json | 354 + .../js-character-generator/package-lock.json | 5460 +++++++++++ samples/js-coffee-shop/package-lock.json | 7957 +++++++++++++++++ samples/js-menu/package-lock.json | 7763 ++++++++++++++++ samples/js-schoolAgent/package-lock.json | 5622 ++++++++++++ samples/js-schoolAgent/package.json | 2 +- samples/prompts/package-lock.json | 5461 +++++++++++ 9 files changed, 32975 insertions(+), 3 deletions(-) create mode 100644 samples/chatbot/package-lock.json create mode 100644 samples/js-angular/package-lock.json create mode 100644 samples/js-character-generator/package-lock.json create mode 100644 samples/js-coffee-shop/package-lock.json create mode 100644 samples/js-menu/package-lock.json create mode 100644 samples/js-schoolAgent/package-lock.json create mode 100644 samples/prompts/package-lock.json diff --git a/.gitignore b/.gitignore index 17e22060e..e5ec5dee5 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ dist .vscode .DS_Store package-lock.json +!samples/**/package-lock.json .angular *.tgz .pnpm-store @@ -30,10 +31,10 @@ ui-debug.log firebase-debug.log **/*.env # RAG sample files -!js/testapps/rag/package.json js/testapps/rag/*.json +!js/testapps/rag/package.json js/testapps/evals/__db*.json -js/testapps/menu/rag/__db*.json +js/testapps/menu/**/__db*.json # Test files last_recording.mp4 diff --git a/samples/chatbot/package-lock.json b/samples/chatbot/package-lock.json new file mode 100644 index 000000000..5f7f4265f --- /dev/null +++ b/samples/chatbot/package-lock.json @@ -0,0 +1,354 @@ +{ + "name": "js-angular", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "js-angular", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "concurrently": "^8.2.2" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", + "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "node_modules/concurrently": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "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==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "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==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=12" + } + } + } +} diff --git a/samples/js-angular/package-lock.json b/samples/js-angular/package-lock.json new file mode 100644 index 000000000..5f7f4265f --- /dev/null +++ b/samples/js-angular/package-lock.json @@ -0,0 +1,354 @@ +{ + "name": "js-angular", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "js-angular", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "concurrently": "^8.2.2" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", + "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "node_modules/concurrently": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "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==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "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==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=12" + } + } + } +} diff --git a/samples/js-character-generator/package-lock.json b/samples/js-character-generator/package-lock.json new file mode 100644 index 000000000..6965cd6ac --- /dev/null +++ b/samples/js-character-generator/package-lock.json @@ -0,0 +1,5460 @@ +{ + "name": "Character Generator", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "Character Generator", + "version": "0.1.0", + "license": "ISC", + "dependencies": { + "@genkit-ai/googleai": "^0.9", + "genkit": "^0.9" + }, + "devDependencies": { + "genkit-cli": "^0.9", + "tsx": "^4.19.2", + "typescript": "^5.6.3" + } + }, + "node_modules/@asteasolutions/zod-to-openapi": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.0.tgz", + "integrity": "sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==", + "dev": true, + "dependencies": { + "openapi3-ts": "^4.1.2" + }, + "peerDependencies": { + "zod": "^3.20.2" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@genkit-ai/ai": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-0.9.12.tgz", + "integrity": "sha512-xyVVAIGKNpj5zCkoEfWZkzwctl0/hmpX6vKZgdgMH2MiqP5LzTp7rUekBMon8c1rMDVAze97QVSjAmZIoMLSlA==", + "dependencies": { + "@genkit-ai/core": "0.9.12", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.11.19", + "colorette": "^2.0.20", + "json5": "^2.2.3", + "node-fetch": "^3.3.2", + "partial-json": "^0.1.7", + "uuid": "^10.0.0" + } + }, + "node_modules/@genkit-ai/core": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-0.9.12.tgz", + "integrity": "sha512-QPJZ3TL5Iq2fyeo30MpUjd3ZLcYQf97RsitDZhMbGy3vMwbgig0nhEbJ6v/qaWsOMqSfIxJE/gETY3mMts1vRg==", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "async-mutex": "^0.5.0", + "body-parser": "^1.20.3", + "cors": "^2.8.5", + "express": "^4.21.0", + "get-port": "^5.1.0", + "json-schema": "^0.4.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/dotprompt": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/dotprompt/-/dotprompt-0.9.12.tgz", + "integrity": "sha512-eEHBRzRVemiPuqCBbXiLgltNWpmCHmC+gVHBhsAnrfOYBlwmPvh2nnAPBXGYnkDH87PuN11jg8YJQmO4kQuoSw==", + "dependencies": { + "@genkit-ai/ai": "0.9.12", + "@genkit-ai/core": "0.9.12", + "front-matter": "^4.0.2", + "handlebars": "^4.7.8", + "node-fetch": "^3.3.2" + } + }, + "node_modules/@genkit-ai/googleai": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-0.9.12.tgz", + "integrity": "sha512-q6bX9Nq4xVpzH4vd9W+rg4xO7SVodG516K4BVM04DtxU5w0ogwLl/mRPEU0VIoISjD6FwaC4sPjPkEerTp8a4g==", + "dependencies": { + "@google/generative-ai": "^0.21.0", + "google-auth-library": "^9.6.3", + "node-fetch": "^3.3.2" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/@genkit-ai/telemetry-server": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-0.9.12.tgz", + "integrity": "sha512-8w1O9LOUtMCGc8bx+Vcd5D68L8ctM6H544Vxnfvy8gx4JHV5dj7vQiI9YDgm65pgP15pytQzt1TcRtOtYoimFw==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@genkit-ai/tools-common": "0.9.12", + "@google-cloud/firestore": "^7.6.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "async-mutex": "^0.5.0", + "express": "^4.21.0", + "zod": "^3.22.4" + } + }, + "node_modules/@genkit-ai/tools-common": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-0.9.12.tgz", + "integrity": "sha512-A9ToM/CY6vxcBSw4o47Q7qrpMaRsk4P6ZuzoDMlCzCVI+RZN5ulTL4LXMrq6nQ/KxY1GT94bSoulROXBMzW/Gw==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@trpc/server": "10.45.0", + "adm-zip": "^0.5.12", + "axios": "^1.7.7", + "body-parser": "^1.20.2", + "chokidar": "^3.5.3", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "configstore": "^5.0.1", + "express": "^4.21.0", + "get-port": "5.1.1", + "glob": "^10.3.12", + "inquirer": "^8.2.0", + "js-yaml": "^4.1.0", + "json-2-csv": "^5.5.1", + "json-schema": "^0.4.0", + "terminate": "^2.6.1", + "tsx": "^4.19.2", + "uuid": "^9.0.1", + "winston": "^3.11.0", + "yaml": "^2.4.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/tools-common/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==", + "dev": true + }, + "node_modules/@genkit-ai/tools-common/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@genkit-ai/tools-common/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@google-cloud/firestore": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.0.tgz", + "integrity": "sha512-88uZ+jLsp1aVMj7gh3EKYH1aulTAMFAp8sH/v5a9w8q8iqSG27RiWLoxSAFr/XocZ9hGiWH1kEnBw+zl3xAgNA==", + "dev": true, + "dependencies": { + "@opentelemetry/api": "^1.3.0", + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.3.3", + "protobufjs": "^7.2.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google/generative-ai": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz", + "integrity": "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz", + "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz", + "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz", + "integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", + "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz", + "integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz", + "integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz", + "integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", + "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", + "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz", + "integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", + "@opentelemetry/exporter-zipkin": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/sdk-trace-node": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", + "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.25.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/propagator-b3": "1.25.1", + "@opentelemetry/propagator-jaeger": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trpc/server": { + "version": "10.45.0", + "resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.45.0.tgz", + "integrity": "sha512-2Fwzv6nqpE0Ie/G7PeS0EVR89zLm+c1Mw7T+RAGtU807j4oaUx0zGkBXTu5u9AI+j+BYNN2GZxJcuDTAecbr1A==", + "dev": true, + "funding": [ + "https://trpc.io/sponsor" + ] + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "dev": true + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.17.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", + "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "dev": true, + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "dev": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "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==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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==", + "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==" + }, + "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==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deeks": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/deeks/-/deeks-3.1.0.tgz", + "integrity": "sha512-e7oWH1LzIdv/prMQ7pmlDlaVoL64glqzvNgkgQNgyec9ORPHrT2jaOqMtRyqJuwWjtfb6v+2rk9pmaHj+F137A==", + "dev": true, + "engines": { + "node": ">= 16" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/doc-path": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-4.1.1.tgz", + "integrity": "sha512-h1ErTglQAVv2gCnOpD3sFS6uolDbOKHDU1BZq+Kl3npPqroU3dYL42lUgMfd5UimlwtRgp7C9dLGwqQ5D2HYgQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "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==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/extract-zip/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==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, + "node_modules/front-matter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", + "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", + "dependencies": { + "js-yaml": "^3.13.1" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "dependencies": { + "gaxios": "^6.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/genkit": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-0.9.12.tgz", + "integrity": "sha512-m1VQE/yhuii0y1aGTnkoSesSXTNE25q1s7vv5YVgJWa/t2gOXuznZOoHTJ847f/3mKC7fgnV7xGI+t/+7wbe0g==", + "dependencies": { + "@genkit-ai/ai": "0.9.12", + "@genkit-ai/core": "0.9.12", + "@genkit-ai/dotprompt": "0.9.12", + "uuid": "^10.0.0" + } + }, + "node_modules/genkit-cli": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-0.9.12.tgz", + "integrity": "sha512-Qsk7TmVBxghTT+0cRe2j0fYJboUIfWhuUoeVbWshranye6tConGMyDEZdXBIeMWIixSbG7k8a7a2INdRdjcQJw==", + "dev": true, + "dependencies": { + "@genkit-ai/telemetry-server": "0.9.12", + "@genkit-ai/tools-common": "0.9.12", + "axios": "^1.7.7", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "extract-zip": "^2.0.1", + "get-port": "5.1.1", + "inquirer": "^8.2.0", + "open": "^6.3.0", + "ora": "^5.4.1" + }, + "bin": { + "genkit": "dist/bin/genkit.js" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", + "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", + "dev": true, + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/google-gax/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/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==", + "dev": true + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/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==" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/import-in-the-middle": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.12.0.tgz", + "integrity": "sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-2-csv": { + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.8.tgz", + "integrity": "sha512-eMQHOwV+av8Sgo+fkbEbQWOw/kwh89AZ5fNA8TYfcooG6TG1ZOL2WcPUrngIMIK8dBJitQ8QEU0zbncQ0CX4CQ==", + "dev": true, + "dependencies": { + "deeks": "3.1.0", + "doc-path": "4.1.1" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dev": true, + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/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==", + "dev": true + }, + "node_modules/long": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openapi3-ts": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.4.0.tgz", + "integrity": "sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==", + "dev": true, + "dependencies": { + "yaml": "^2.5.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "dev": true, + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.0.tgz", + "integrity": "sha512-/Tvpny/RVVicqlYTKwt/GtpZRsPG1CmJNhxVKGz+Sy/4MONfXCVNK69MFgGKdUt0/324q3ClI2dICcPgISrC8g==", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/require-in-the-middle/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/require-in-the-middle/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==" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "dev": true, + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/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==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "dev": true, + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "dev": true + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/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==", + "dev": true + }, + "node_modules/teeny-request/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/terminate": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/terminate/-/terminate-2.8.0.tgz", + "integrity": "sha512-bcbjJEg0wY5nuJXvGxxHfmoEPkyHLCctUKO6suwtxy7jVSgGcgPeGwpbLDLELFhIaxCGRr3dPvyNg1yuz2V0eg==", + "dev": true, + "dependencies": { + "ps-tree": "^1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true, + "engines": { + "node": ">= 14.0.0" + } + }, + "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==" + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "dev": true, + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dev": true, + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", + "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} diff --git a/samples/js-coffee-shop/package-lock.json b/samples/js-coffee-shop/package-lock.json new file mode 100644 index 000000000..527c1557b --- /dev/null +++ b/samples/js-coffee-shop/package-lock.json @@ -0,0 +1,7957 @@ +{ + "name": "coffee-shop", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "coffee-shop", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@genkit-ai/dev-local-vectorstore": "^0.9.0-rc || ^0.9", + "@genkit-ai/evaluator": "^0.9.0-rc || ^0.9", + "@genkit-ai/firebase": "^0.9.0-rc || ^0.9", + "@genkit-ai/googleai": "^0.9.0-rc || ^0.9", + "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9", + "genkit": "^0.9.0-rc || ^0.9", + "genkitx-chromadb": "^0.9.0-rc || ^0.9", + "genkitx-ollama": "^0.9.0-rc || ^0.9", + "genkitx-pinecone": "^0.9.0-rc || ^0.9", + "zod": "^3.22.4" + }, + "devDependencies": { + "genkit-cli": "^0.9.0-rc || ^0.9", + "rimraf": "^6.0.1", + "typescript": "^5.3.3" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.24.3", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.24.3.tgz", + "integrity": "sha512-916wJXO6T6k8R6BAAcLhLPv/pnLGy7YSEBZXZ1XTFbLcTZE8oTy3oDW9WJf9KKZwMvVcePIfoTSvzXHRcGxkQQ==", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7", + "web-streams-polyfill": "^3.2.1" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/@types/node": { + "version": "18.19.74", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", + "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/@anthropic-ai/vertex-sdk": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@anthropic-ai/vertex-sdk/-/vertex-sdk-0.4.3.tgz", + "integrity": "sha512-2Uef0C5P2Hx+T88RnUSRA3u4aZqmqnrRSOb2N64ozgKPiSUPTM5JlggAq2b32yWMj5d3MLYa6spJXKMmHXOcoA==", + "dependencies": { + "@anthropic-ai/sdk": ">=0.14 <1", + "google-auth-library": "^9.4.2" + } + }, + "node_modules/@asteasolutions/zod-to-openapi": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.0.tgz", + "integrity": "sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==", + "dev": true, + "dependencies": { + "openapi3-ts": "^4.1.2" + }, + "peerDependencies": { + "zod": "^3.20.2" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@fastify/busboy": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", + "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==" + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==" + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==" + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==" + }, + "node_modules/@firebase/component": { + "version": "0.6.12", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.12.tgz", + "integrity": "sha512-YnxqjtohLbnb7raXt2YuA44cC1wA9GiehM/cmxrsoxKlFxBLy2V0OkRSj9gpngAE0UoJ421Wlav9ycO7lTPAUw==", + "dependencies": { + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.11.tgz", + "integrity": "sha512-gLrw/XeioswWUXgpVKCPAzzoOuvYNqK5fRUeiJTzO7Mlp9P6ylFEyPJlRBl1djqYye641r3MX6AmIeMXwjgwuQ==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.2.tgz", + "integrity": "sha512-5zvdnMsfDHvrQAVM6jBS7CkBpu+z3YbpFdhxRsrK1FP45IEfxlzpeuEUb17D/tpM10vfq4Ok0x5akIBaCv7gfA==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/database": "1.0.11", + "@firebase/database-types": "1.0.8", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.8.tgz", + "integrity": "sha512-6lPWIGeufhUq1heofZULyVvWFhD01TUrkkB9vyhmksjZ4XF7NaivQp9rICMk7QNhqwa+uDCaj4j+Q8qqcSVZ9g==", + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.10.3" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", + "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/util": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.3.tgz", + "integrity": "sha512-wfoF5LTy0m2ufUapV0ZnpcGQvuavTbJ5Qr1Ze9OJGL70cSMvhDyjS4w2121XdA3lGZSTOsDOyGhpoDtYwck85A==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@genkit-ai/ai": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-0.9.12.tgz", + "integrity": "sha512-xyVVAIGKNpj5zCkoEfWZkzwctl0/hmpX6vKZgdgMH2MiqP5LzTp7rUekBMon8c1rMDVAze97QVSjAmZIoMLSlA==", + "dependencies": { + "@genkit-ai/core": "0.9.12", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.11.19", + "colorette": "^2.0.20", + "json5": "^2.2.3", + "node-fetch": "^3.3.2", + "partial-json": "^0.1.7", + "uuid": "^10.0.0" + } + }, + "node_modules/@genkit-ai/core": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-0.9.12.tgz", + "integrity": "sha512-QPJZ3TL5Iq2fyeo30MpUjd3ZLcYQf97RsitDZhMbGy3vMwbgig0nhEbJ6v/qaWsOMqSfIxJE/gETY3mMts1vRg==", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "async-mutex": "^0.5.0", + "body-parser": "^1.20.3", + "cors": "^2.8.5", + "express": "^4.21.0", + "get-port": "^5.1.0", + "json-schema": "^0.4.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/dev-local-vectorstore": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/dev-local-vectorstore/-/dev-local-vectorstore-0.9.12.tgz", + "integrity": "sha512-wkrLRqTw1N50hZpQlC1Hq8yOWapyz3BiC03auK93j85sL2L6tIFQ1zFHw7qKD+t39fiTmpocl8O6spLEerUKoA==", + "dependencies": { + "compute-cosine-similarity": "^1.1.0", + "ts-md5": "^1.3.1" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/@genkit-ai/dotprompt": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/dotprompt/-/dotprompt-0.9.12.tgz", + "integrity": "sha512-eEHBRzRVemiPuqCBbXiLgltNWpmCHmC+gVHBhsAnrfOYBlwmPvh2nnAPBXGYnkDH87PuN11jg8YJQmO4kQuoSw==", + "dependencies": { + "@genkit-ai/ai": "0.9.12", + "@genkit-ai/core": "0.9.12", + "front-matter": "^4.0.2", + "handlebars": "^4.7.8", + "node-fetch": "^3.3.2" + } + }, + "node_modules/@genkit-ai/evaluator": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/evaluator/-/evaluator-0.9.12.tgz", + "integrity": "sha512-JbZfwwqDf6IP84cGt+QZlSB0h+FcdAM7Ur6A/lL5GJdMudIjRdxmhu3FEu/kWKK59EQlJShGBW5/Vyw93ynPqw==", + "dependencies": { + "@genkit-ai/dotprompt": "0.9.12", + "compute-cosine-similarity": "^1.1.0", + "node-fetch": "^3.3.2", + "path": "^0.12.7" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/@genkit-ai/firebase": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/firebase/-/firebase-0.9.12.tgz", + "integrity": "sha512-PbEAPO3GTENaJR5RUqrq88YIVxgkVi2pc82kOCrkJDpw/+PcyRIZcgvbQYSAzB932yVU0sy33zzdUfg9t2thMA==", + "dependencies": { + "@genkit-ai/google-cloud": "0.9.12", + "express": "^4.21.0", + "google-auth-library": "^9.6.3" + }, + "peerDependencies": { + "@google-cloud/firestore": "^7.6.0", + "firebase-admin": ">=12.2", + "firebase-functions": ">=4.8", + "genkit": "0.9.12" + } + }, + "node_modules/@genkit-ai/google-cloud": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/google-cloud/-/google-cloud-0.9.12.tgz", + "integrity": "sha512-hYDvdQmGdI/JC6hUa6hMs6+Kc04wmi+/54zJIB+EOiNLd6fJ193B+q/pHqI4UxvofJPsf9C6VTK5Q54+yyQm7w==", + "dependencies": { + "@google-cloud/logging-winston": "^6.0.0", + "@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.19.0", + "@google-cloud/opentelemetry-cloud-trace-exporter": "^2.4.1", + "@google-cloud/opentelemetry-resource-util": "^2.4.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/auto-instrumentations-node": "^0.49.1", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/instrumentation-pino": "^0.41.0", + "@opentelemetry/instrumentation-winston": "^0.39.0", + "@opentelemetry/resources": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "google-auth-library": "^9.6.3", + "node-fetch": "^3.3.2", + "winston": "^3.12.0" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/@genkit-ai/googleai": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-0.9.12.tgz", + "integrity": "sha512-q6bX9Nq4xVpzH4vd9W+rg4xO7SVodG516K4BVM04DtxU5w0ogwLl/mRPEU0VIoISjD6FwaC4sPjPkEerTp8a4g==", + "dependencies": { + "@google/generative-ai": "^0.21.0", + "google-auth-library": "^9.6.3", + "node-fetch": "^3.3.2" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/@genkit-ai/telemetry-server": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-0.9.12.tgz", + "integrity": "sha512-8w1O9LOUtMCGc8bx+Vcd5D68L8ctM6H544Vxnfvy8gx4JHV5dj7vQiI9YDgm65pgP15pytQzt1TcRtOtYoimFw==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@genkit-ai/tools-common": "0.9.12", + "@google-cloud/firestore": "^7.6.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "async-mutex": "^0.5.0", + "express": "^4.21.0", + "zod": "^3.22.4" + } + }, + "node_modules/@genkit-ai/tools-common": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-0.9.12.tgz", + "integrity": "sha512-A9ToM/CY6vxcBSw4o47Q7qrpMaRsk4P6ZuzoDMlCzCVI+RZN5ulTL4LXMrq6nQ/KxY1GT94bSoulROXBMzW/Gw==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@trpc/server": "10.45.0", + "adm-zip": "^0.5.12", + "axios": "^1.7.7", + "body-parser": "^1.20.2", + "chokidar": "^3.5.3", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "configstore": "^5.0.1", + "express": "^4.21.0", + "get-port": "5.1.1", + "glob": "^10.3.12", + "inquirer": "^8.2.0", + "js-yaml": "^4.1.0", + "json-2-csv": "^5.5.1", + "json-schema": "^0.4.0", + "terminate": "^2.6.1", + "tsx": "^4.19.2", + "uuid": "^9.0.1", + "winston": "^3.11.0", + "yaml": "^2.4.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/tools-common/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==", + "dev": true + }, + "node_modules/@genkit-ai/tools-common/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@genkit-ai/tools-common/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@genkit-ai/vertexai": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/vertexai/-/vertexai-0.9.12.tgz", + "integrity": "sha512-lA6lwh5jgYgFr7RsFCXs+iASUZI2CmrGQEJCR+/qKJTNG0OyI4O7CdoT5SZSc1fK7oXeWH0/4x6mIwcngjy2Gw==", + "dependencies": { + "@anthropic-ai/sdk": "^0.24.3", + "@anthropic-ai/vertex-sdk": "^0.4.0", + "@google-cloud/aiplatform": "^3.23.0", + "@google-cloud/vertexai": "^1.9.0", + "@mistralai/mistralai-gcp": "^1.3.5", + "google-auth-library": "^9.14.2", + "googleapis": "^140.0.1", + "node-fetch": "^3.3.2", + "openai": "^4.52.7" + }, + "optionalDependencies": { + "@google-cloud/bigquery": "^7.8.0", + "firebase-admin": ">=12.2" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/@google-cloud/aiplatform": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/@google-cloud/aiplatform/-/aiplatform-3.34.0.tgz", + "integrity": "sha512-Ii1CXJ59g5hcVYNZOx08XBV5nq0JIOSo2I9uC/WYkdXWekc3XSV9emRz8pKOQSULzrTOTnD80N4re49S07xfyQ==", + "dependencies": { + "google-gax": "^4.0.3", + "protobuf.js": "^1.1.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/bigquery": { + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@google-cloud/bigquery/-/bigquery-7.9.1.tgz", + "integrity": "sha512-ZkcRMpBoFLxIh6TiQBywA22yT3c2j0f07AHWEMjtYqMQzZQbFrpxuJU2COp3tyjZ91ZIGHe4gY7/dGZL88cltg==", + "optional": true, + "dependencies": { + "@google-cloud/common": "^5.0.0", + "@google-cloud/paginator": "^5.0.2", + "@google-cloud/precise-date": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "arrify": "^2.0.1", + "big.js": "^6.0.0", + "duplexify": "^4.0.0", + "extend": "^3.0.2", + "is": "^3.3.0", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/bigquery/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@google-cloud/common": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-5.0.2.tgz", + "integrity": "sha512-V7bmBKYQyu0eVG2BFejuUjlBt+zrya6vtsKdY+JxMM/dNntPF41vZ9+LhOshEUH01zOHEqBSvI7Dad7ZS6aUeA==", + "dependencies": { + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "arrify": "^2.0.1", + "duplexify": "^4.1.1", + "extend": "^3.0.2", + "google-auth-library": "^9.0.0", + "html-entities": "^2.5.2", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/firestore": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.0.tgz", + "integrity": "sha512-88uZ+jLsp1aVMj7gh3EKYH1aulTAMFAp8sH/v5a9w8q8iqSG27RiWLoxSAFr/XocZ9hGiWH1kEnBw+zl3xAgNA==", + "dependencies": { + "@opentelemetry/api": "^1.3.0", + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.3.3", + "protobufjs": "^7.2.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/logging": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@google-cloud/logging/-/logging-11.2.0.tgz", + "integrity": "sha512-Ma94jvuoMpbgNniwtelOt8w82hxK62FuOXZonEv0Hyk3B+/YVuLG/SWNyY9yMso/RXnPEc1fP2qo9kDrjf/b2w==", + "dependencies": { + "@google-cloud/common": "^5.0.0", + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "@opentelemetry/api": "^1.7.0", + "arrify": "^2.0.1", + "dot-prop": "^6.0.0", + "eventid": "^2.0.0", + "extend": "^3.0.2", + "gcp-metadata": "^6.0.0", + "google-auth-library": "^9.0.0", + "google-gax": "^4.0.3", + "on-finished": "^2.3.0", + "pumpify": "^2.0.1", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/logging-winston": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/logging-winston/-/logging-winston-6.0.0.tgz", + "integrity": "sha512-/lVp7CyT3nFOr+AjQlZnJhTIOf+kcNGB4JTziL0fkX6Ov/2qNKtRGS/NqE6cD+VSPiv5jLOty3LgkRsXMpYxQQ==", + "dependencies": { + "@google-cloud/logging": "^11.0.0", + "google-auth-library": "^9.0.0", + "lodash.mapvalues": "^4.6.0", + "winston-transport": "^4.3.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "winston": ">=3.2.1" + } + }, + "node_modules/@google-cloud/logging/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@google-cloud/opentelemetry-cloud-monitoring-exporter": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@google-cloud/opentelemetry-cloud-monitoring-exporter/-/opentelemetry-cloud-monitoring-exporter-0.19.0.tgz", + "integrity": "sha512-5SOPXwC6RET4ZvXxw5D97dp8fWpqWEunHrzrUUGXhG4UAeedQe1KvYV8CK+fnaAbN2l2ha6QDYspT6z40TVY0g==", + "dependencies": { + "@google-cloud/opentelemetry-resource-util": "^2.3.0", + "@google-cloud/precise-date": "^4.0.0", + "google-auth-library": "^9.0.0", + "googleapis": "^137.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/resources": "^1.0.0", + "@opentelemetry/sdk-metrics": "^1.0.0" + } + }, + "node_modules/@google-cloud/opentelemetry-cloud-monitoring-exporter/node_modules/googleapis": { + "version": "137.1.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-137.1.0.tgz", + "integrity": "sha512-2L7SzN0FLHyQtFmyIxrcXhgust77067pkkduqkbIpDuj9JzVnByxsRrcRfUMFQam3rQkWW2B0f1i40IwKDWIVQ==", + "dependencies": { + "google-auth-library": "^9.0.0", + "googleapis-common": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/opentelemetry-cloud-trace-exporter": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@google-cloud/opentelemetry-cloud-trace-exporter/-/opentelemetry-cloud-trace-exporter-2.4.1.tgz", + "integrity": "sha512-Dq2IyAyA9PCjbjLOn86i2byjkYPC59b5ic8k/L4q5bBWH0Jro8lzMs8C0G5pJfqh2druj8HF+oAIAlSdWQ+Z9Q==", + "dependencies": { + "@google-cloud/opentelemetry-resource-util": "^2.4.0", + "@grpc/grpc-js": "^1.1.8", + "@grpc/proto-loader": "^0.7.0", + "google-auth-library": "^9.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/resources": "^1.0.0", + "@opentelemetry/sdk-trace-base": "^1.0.0" + } + }, + "node_modules/@google-cloud/opentelemetry-resource-util": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@google-cloud/opentelemetry-resource-util/-/opentelemetry-resource-util-2.4.0.tgz", + "integrity": "sha512-/7ujlMoKtDtrbQlJihCjQnm31n2s2RTlvJqcSbt2jV3OkCzPAdo3u31Q13HNugqtIRUSk7bUoLx6AzhURkhW4w==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.22.0", + "gcp-metadata": "^6.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/resources": "^1.0.0" + } + }, + "node_modules/@google-cloud/paginator": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", + "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/precise-date": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-4.0.0.tgz", + "integrity": "sha512-1TUx3KdaU3cN7nfCdNf+UVqA/PSX29Cjcox3fZZBtINlRrXVTmUkQnCKv2MbBUbCopbK4olAT1IHl76uZyCiVA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/projectify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", + "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/promisify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", + "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@google-cloud/storage": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.15.0.tgz", + "integrity": "sha512-/j/+8DFuEOo33fbdX0V5wjooOoFahEaMEdImHBmM2tH9MPHJYNtmXOf2sGUmZmiufSukmBEvdlzYgDkkgeBiVQ==", + "optional": true, + "dependencies": { + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "abort-controller": "^3.0.0", + "async-retry": "^1.3.3", + "duplexify": "^4.1.3", + "fast-xml-parser": "^4.4.1", + "gaxios": "^6.0.2", + "google-auth-library": "^9.6.3", + "html-entities": "^2.5.2", + "mime": "^3.0.0", + "p-limit": "^3.0.1", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google-cloud/storage/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@google-cloud/vertexai": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@google-cloud/vertexai/-/vertexai-1.9.2.tgz", + "integrity": "sha512-pJSUG3r5QIvCFNfkz7/y7kEqvEJaVAk0jZbZoKbcPCRUnXaUeAq7p8I0oklqetGyxbUcZ2FOGpt+Y+4uIltVPg==", + "dependencies": { + "google-auth-library": "^9.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@google/generative-ai": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz", + "integrity": "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz", + "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@mistralai/mistralai-gcp": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai-gcp/-/mistralai-gcp-1.4.0.tgz", + "integrity": "sha512-QYBbR/T1U4qZ88m6l5RpOlhly2mXWsXi0owicSX6zt6pBaMORxRs6ZRLmaYX5BNjGqxQUl+LtsSwpMtXW2uU2A==", + "dependencies": { + "google-auth-library": "^9.11.0" + }, + "peerDependencies": { + "zod": ">= 3" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz", + "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node": { + "version": "0.49.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.49.2.tgz", + "integrity": "sha512-xtETEPmAby/3MMmedv8Z/873sdLTWg+Vq98rtm4wbwvAiXBB/ao8qRyzRlvR2MR6puEr+vIB/CXeyJnzNA3cyw==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/instrumentation-amqplib": "^0.41.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.43.0", + "@opentelemetry/instrumentation-aws-sdk": "^0.43.1", + "@opentelemetry/instrumentation-bunyan": "^0.40.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.40.0", + "@opentelemetry/instrumentation-connect": "^0.38.0", + "@opentelemetry/instrumentation-cucumber": "^0.8.0", + "@opentelemetry/instrumentation-dataloader": "^0.11.0", + "@opentelemetry/instrumentation-dns": "^0.38.0", + "@opentelemetry/instrumentation-express": "^0.41.1", + "@opentelemetry/instrumentation-fastify": "^0.38.0", + "@opentelemetry/instrumentation-fs": "^0.14.0", + "@opentelemetry/instrumentation-generic-pool": "^0.38.1", + "@opentelemetry/instrumentation-graphql": "^0.42.0", + "@opentelemetry/instrumentation-grpc": "^0.52.0", + "@opentelemetry/instrumentation-hapi": "^0.40.0", + "@opentelemetry/instrumentation-http": "^0.52.0", + "@opentelemetry/instrumentation-ioredis": "^0.42.0", + "@opentelemetry/instrumentation-kafkajs": "^0.2.0", + "@opentelemetry/instrumentation-knex": "^0.39.0", + "@opentelemetry/instrumentation-koa": "^0.42.0", + "@opentelemetry/instrumentation-lru-memoizer": "^0.39.0", + "@opentelemetry/instrumentation-memcached": "^0.38.0", + "@opentelemetry/instrumentation-mongodb": "^0.46.0", + "@opentelemetry/instrumentation-mongoose": "^0.41.0", + "@opentelemetry/instrumentation-mysql": "^0.40.0", + "@opentelemetry/instrumentation-mysql2": "^0.40.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.39.0", + "@opentelemetry/instrumentation-net": "^0.38.0", + "@opentelemetry/instrumentation-pg": "^0.43.0", + "@opentelemetry/instrumentation-pino": "^0.41.0", + "@opentelemetry/instrumentation-redis": "^0.41.0", + "@opentelemetry/instrumentation-redis-4": "^0.41.1", + "@opentelemetry/instrumentation-restify": "^0.40.0", + "@opentelemetry/instrumentation-router": "^0.39.0", + "@opentelemetry/instrumentation-socket.io": "^0.41.0", + "@opentelemetry/instrumentation-tedious": "^0.13.0", + "@opentelemetry/instrumentation-undici": "^0.5.0", + "@opentelemetry/instrumentation-winston": "^0.39.0", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.29.0", + "@opentelemetry/resource-detector-aws": "^1.6.0", + "@opentelemetry/resource-detector-azure": "^0.2.10", + "@opentelemetry/resource-detector-container": "^0.4.0", + "@opentelemetry/resource-detector-gcp": "^0.29.10", + "@opentelemetry/resources": "^1.24.0", + "@opentelemetry/sdk-node": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.4.1" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz", + "integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", + "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz", + "integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz", + "integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-amqplib": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.41.0.tgz", + "integrity": "sha512-00Oi6N20BxJVcqETjgNzCmVKN+I5bJH/61IlHiIWd00snj1FdgiIKlpE4hYVacTB2sjIBB3nTbHskttdZEE2eg==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-lambda": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.43.0.tgz", + "integrity": "sha512-pSxcWlsE/pCWQRIw92sV2C+LmKXelYkjkA7C5s39iPUi4pZ2lA1nIiw+1R/y2pDEhUHcaKkNyljQr3cx9ZpVlQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/propagator-aws-xray": "^1.3.1", + "@opentelemetry/resources": "^1.8.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "@types/aws-lambda": "8.10.122" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-sdk": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.43.1.tgz", + "integrity": "sha512-qLT2cCniJ5W+6PFzKbksnoIQuq9pS83nmgaExfUwXVvlwi0ILc50dea0tWBHZMkdIDa/zZdcuFrJ7+fUcSnRow==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/propagation-utils": "^0.30.10", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-bunyan": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.40.0.tgz", + "integrity": "sha512-aZ4cXaGWwj79ZXSYrgFVsrDlE4mmf2wfvP9bViwRc0j75A6eN6GaHYHqufFGMTCqASQn5pIjjP+Bx+PWTGiofw==", + "dependencies": { + "@opentelemetry/api-logs": "^0.52.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@types/bunyan": "1.8.9" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cassandra-driver": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.40.0.tgz", + "integrity": "sha512-JxbM39JU7HxE9MTKKwi6y5Z3mokjZB2BjwfqYi4B3Y29YO3I42Z7eopG6qq06yWZc+nQli386UDQe0d9xKmw0A==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-connect": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.38.0.tgz", + "integrity": "sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "@types/connect": "3.4.36" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cucumber": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.8.0.tgz", + "integrity": "sha512-ieTm4RBIlZt2brPwtX5aEZYtYnkyqhAVXJI9RIohiBVMe5DxiwCwt+2Exep/nDVqGPX8zRBZUl4AEw423OxJig==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dataloader": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.11.0.tgz", + "integrity": "sha512-27urJmwkH4KDaMJtEv1uy2S7Apk4XbN4AgWMdfMJbi7DnOduJmeuA+DpJCwXB72tEWXo89z5T3hUVJIDiSNmNw==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dns": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.38.0.tgz", + "integrity": "sha512-Um07I0TQXDWa+ZbEAKDFUxFH40dLtejtExDOMLNJ1CL8VmOmA71qx93Qi/QG4tGkiI1XWqr7gF/oiMCJ4m8buQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-express": { + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.41.1.tgz", + "integrity": "sha512-uRx0V3LPGzjn2bxAnV8eUsDT82vT7NTwI0ezEuPMBOTOsnPpGhWdhcdNdhH80sM4TrWrOfXm9HGEdfWE3TRIww==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fastify": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.38.0.tgz", + "integrity": "sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fs": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.14.0.tgz", + "integrity": "sha512-pVc8P5AgliC1DphyyBUgsxXlm2XaPH4BpYvt7rAZDMIqUpRk8gs19SioABtKqqxvFzg5jPtgJfJsdxq0Y+maLw==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-generic-pool": { + "version": "0.38.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.38.1.tgz", + "integrity": "sha512-WvssuKCuavu/hlq661u82UWkc248cyI/sT+c2dEIj6yCk0BUkErY1D+9XOO+PmHdJNE+76i2NdcvQX5rJrOe/w==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-graphql": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.42.0.tgz", + "integrity": "sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.52.1.tgz", + "integrity": "sha512-EdSDiDSAO+XRXk/ZN128qQpBo1I51+Uay/LUPcPQhSRGf7fBPIEUBeOLQiItguGsug5MGOYjql2w/1wCQF3fdQ==", + "dependencies": { + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-hapi": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.40.0.tgz", + "integrity": "sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.52.1.tgz", + "integrity": "sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/semantic-conventions": "1.25.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-ioredis": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.42.0.tgz", + "integrity": "sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.23.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-kafkajs": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.2.0.tgz", + "integrity": "sha512-uKKmhEFd0zR280tJovuiBG7cfnNZT4kvVTvqtHPxQP7nOmRbJstCYHFH13YzjVcKjkmoArmxiSulmZmF7SLIlg==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.24.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-knex": { + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.39.0.tgz", + "integrity": "sha512-lRwTqIKQecPWDkH1KEcAUcFhCaNssbKSpxf4sxRTAROCwrCEnYkjOuqJHV+q1/CApjMTaKu0Er4LBv/6bDpoxA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-koa": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.42.0.tgz", + "integrity": "sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-lru-memoizer": { + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.39.0.tgz", + "integrity": "sha512-eU1Wx1RRTR/2wYXFzH9gcpB8EPmhYlNDIUHzUXjyUE0CAXEJhBLkYNlzdaVCoQDw2neDqS+Woshiia6+emWK9A==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-memcached": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.38.0.tgz", + "integrity": "sha512-tPmyqQEZNyrvg6G+iItdlguQEcGzfE+bJkpQifmBXmWBnoS5oU3UxqtyYuXGL2zI9qQM5yMBHH4nRXWALzy7WA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.23.0", + "@types/memcached": "^2.2.6" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.46.0.tgz", + "integrity": "sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/sdk-metrics": "^1.9.1", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongoose": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.41.0.tgz", + "integrity": "sha512-ivJg4QnnabFxxoI7K8D+in7hfikjte38sYzJB9v1641xJk9Esa7jM3hmbPB7lxwcgWJLVEDvfPwobt1if0tXxA==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.40.0.tgz", + "integrity": "sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "@types/mysql": "2.15.22" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql2": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.40.0.tgz", + "integrity": "sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "@opentelemetry/sql-common": "^0.40.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-nestjs-core": { + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.39.0.tgz", + "integrity": "sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.23.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-net": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.38.0.tgz", + "integrity": "sha512-stjow1PijcmUquSmRD/fSihm/H61DbjPlJuJhWUe7P22LFPjFhsrSeiB5vGj3vn+QGceNAs+kioUTzMGPbNxtg==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.23.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.43.0.tgz", + "integrity": "sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "@opentelemetry/sql-common": "^0.40.1", + "@types/pg": "8.6.1", + "@types/pg-pool": "2.0.4" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pino": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.41.0.tgz", + "integrity": "sha512-Kpv0fJRk/8iMzMk5Ue5BsUJfHkBJ2wQoIi/qduU1a1Wjx9GLj6J2G17PHjPK5mnZjPNzkFOXFADZMfgDioliQw==", + "dependencies": { + "@opentelemetry/api-logs": "^0.52.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.41.0.tgz", + "integrity": "sha512-RJ1pwI3btykp67ts+5qZbaFSAAzacucwBet5/5EsKYtWBpHbWwV/qbGN/kIBzXg5WEZBhXLrR/RUq0EpEUpL3A==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis-4": { + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.41.1.tgz", + "integrity": "sha512-UqJAbxraBk7s7pQTlFi5ND4sAUs4r/Ai7gsAVZTQDbHl2kSsOp7gpHcpIuN5dpcI2xnuhM2tkH4SmEhbrv2S6Q==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-restify": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.40.0.tgz", + "integrity": "sha512-sm/rH/GysY/KOEvZqYBZSLYFeXlBkHCgqPDgWc07tz+bHCN6mPs9P3otGOSTe7o3KAIM8Nc6ncCO59vL+jb2cA==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-router": { + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.39.0.tgz", + "integrity": "sha512-LaXnVmD69WPC4hNeLzKexCCS19hRLrUw3xicneAMkzJSzNJvPyk7G6I7lz7VjQh1cooObPBt9gNyd3hhTCUrag==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-socket.io": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.41.0.tgz", + "integrity": "sha512-7fzDe9/FpO6NFizC/wnzXXX7bF9oRchsD//wFqy5g5hVEgXZCQ70IhxjrKdBvgjyIejR9T9zTvfQ6PfVKfkCAw==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-tedious": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.13.0.tgz", + "integrity": "sha512-Pob0+0R62AqXH50pjazTeGBy/1+SK4CYpFUBV5t7xpbpeuQezkkgVGvLca84QqjBqQizcXedjpUJLgHQDixPQg==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "@types/tedious": "^4.0.14" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.5.0.tgz", + "integrity": "sha512-aNTeSrFAVcM9qco5DfZ9DNXu6hpMRe8Kt8nCDHfMWDB3pwgGVUE76jTdohc+H/7eLRqh4L7jqs5NSQoHw7S6ww==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.7.0" + } + }, + "node_modules/@opentelemetry/instrumentation-winston": { + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.39.0.tgz", + "integrity": "sha512-v/1xziLJ9CyB3YDjBSBzbB70Qd0JwWTo36EqWK5m3AR0CzsyMQQmf3ZIZM6sgx7hXMcRQ0pnEYhg6nhrUQPm9A==", + "dependencies": { + "@opentelemetry/api-logs": "^0.52.0", + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz", + "integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagation-utils": { + "version": "0.30.15", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.30.15.tgz", + "integrity": "sha512-nQ30K+eXTkd9Kt8yep9FPrqogS712GvdkV6R1T+xZMSZnFrRCyZuWxMtP3+s3hrK2HWw3ti4lsIfBzsHWYiyrA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/propagator-aws-xray": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-1.26.1.tgz", + "integrity": "sha512-rMffLq+rPJrejFPv40MQGXFroAfyg9zfVzO0Jy4wbfEkSJ4uOIeC8T2aAf7Z5igZ7nPZsQJ7xLpbCSq8MnQziQ==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", + "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", + "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/redis-common": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz", + "integrity": "sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { + "version": "0.29.7", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.29.7.tgz", + "integrity": "sha512-PExUl/R+reSQI6Y/eNtgAsk6RHk1ElYSzOa8/FHfdc/nLmx9sqMasBEpLMkETkzDP7t27ORuXe4F9vwkV2uwwg==", + "dependencies": { + "@opentelemetry/core": "^1.26.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-aws": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-1.11.0.tgz", + "integrity": "sha512-j7qQ75enAJrlSPkPowasScuukZ2ffFG659rhxOpUM4dBe/O8Jpq+dy4pIdFtjWKkM9i7LgisdUt/GW7wGIWoEQ==", + "dependencies": { + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-azure": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.2.12.tgz", + "integrity": "sha512-iIarQu6MiCjEEp8dOzmBvCSlRITPFTinFB2oNKAjU6xhx8d7eUcjNOKhBGQTvuCriZrxrEvDaEEY9NfrPQ6uYQ==", + "dependencies": { + "@opentelemetry/core": "^1.25.1", + "@opentelemetry/resources": "^1.10.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-container": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.4.4.tgz", + "integrity": "sha512-ZEN2mq7lIjQWJ8NTt1umtr6oT/Kb89856BOmESLSvgSHbIwOFYs7cSfSRH5bfiVw6dXTQAVbZA/wLgCHKrebJA==", + "dependencies": { + "@opentelemetry/core": "^1.26.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-gcp": { + "version": "0.29.13", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.29.13.tgz", + "integrity": "sha512-vdotx+l3Q+89PeyXMgKEGnZ/CwzwMtuMi/ddgD9/5tKZ08DfDGB2Npz9m2oXPHRCjc4Ro6ifMqFlRyzIvgOjhg==", + "dependencies": { + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "gcp-metadata": "^6.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz", + "integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", + "@opentelemetry/exporter-zipkin": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/sdk-trace-node": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", + "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.25.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/propagator-b3": "1.25.1", + "@opentelemetry/propagator-jaeger": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sql-common": { + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz", + "integrity": "sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==", + "dependencies": { + "@opentelemetry/core": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0" + } + }, + "node_modules/@pinecone-database/pinecone": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@pinecone-database/pinecone/-/pinecone-2.2.2.tgz", + "integrity": "sha512-gbe/4SowHc64pHIm0kBdgY9hVdzsQnnnpcWviwYMB33gOmsL8brvE8fUSpl1dLDvdyXzKcQkzdBsjCDlqgpdMA==", + "dependencies": { + "@sinclair/typebox": "^0.29.0", + "ajv": "^8.12.0", + "cross-fetch": "^3.1.5", + "encoding": "^0.1.13" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@sinclair/typebox": { + "version": "0.29.6", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.29.6.tgz", + "integrity": "sha512-aX5IFYWlMa7tQ8xZr3b2gtVReCvg7f3LEhjir/JAjX2bJCMVJA5tIPv30wTD4KDfcwMd7DDYY3hFDeGmOgtrZQ==" + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trpc/server": { + "version": "10.45.0", + "resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.45.0.tgz", + "integrity": "sha512-2Fwzv6nqpE0Ie/G7PeS0EVR89zLm+c1Mw7T+RAGtU807j4oaUx0zGkBXTu5u9AI+j+BYNN2GZxJcuDTAecbr1A==", + "dev": true, + "funding": [ + "https://trpc.io/sponsor" + ] + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.122", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.122.tgz", + "integrity": "sha512-vBkIh9AY22kVOCEKo5CJlyCgmSWvasC+SWUxL/x/vOwRobMpI/HG1xp/Ae3AqmSiZeLUbOhW0FCD3ZjqqUxmXw==" + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bunyan": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.9.tgz", + "integrity": "sha512-ZqS9JGpBxVOvsawzmVt30sP++gSQMTejCkIAQ3VdadOcRE8izTyW66hufvwLeH+YEGP6Js2AW7Gz+RMyvrEbmw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==" + }, + "node_modules/@types/connect": { + "version": "3.4.36", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", + "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.8.tgz", + "integrity": "sha512-7fx54m60nLFUVYlxAB1xpe9CBWX2vSrk50Y6ogRJ1v5xxtba7qXTg5BgYDN5dq+yuQQ9HaVlHJyAAt1/mxryFg==", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, + "node_modules/@types/memcached": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.10.tgz", + "integrity": "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + }, + "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==" + }, + "node_modules/@types/mysql": { + "version": "2.15.22", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.22.tgz", + "integrity": "sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.17.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", + "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/pg": { + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", + "integrity": "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/pg-pool": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.4.tgz", + "integrity": "sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==", + "dependencies": { + "@types/pg": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==" + }, + "node_modules/@types/tedious": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", + "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "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==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "optional": true, + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/big.js": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.2.tgz", + "integrity": "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==", + "optional": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bigjs" + } + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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==", + "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==" + }, + "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==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/compute-cosine-similarity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-cosine-similarity/-/compute-cosine-similarity-1.1.0.tgz", + "integrity": "sha512-FXhNx0ILLjGi9Z9+lglLzM12+0uoTnYkHm7GiadXDAr0HGVLm25OivUS1B/LPkbzzvlcXz/1EvWg9ZYyJSdhTw==", + "dependencies": { + "compute-dot": "^1.1.0", + "compute-l2norm": "^1.1.0", + "validate.io-array": "^1.0.5", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/compute-dot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-dot/-/compute-dot-1.1.0.tgz", + "integrity": "sha512-L5Ocet4DdMrXboss13K59OK23GXjiSia7+7Ukc7q4Bl+RVpIXK2W9IHMbWDZkh+JUEvJAwOKRaJDiFUa1LTnJg==", + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/compute-l2norm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-l2norm/-/compute-l2norm-1.1.0.tgz", + "integrity": "sha512-6EHh1Elj90eU28SXi+h2PLnTQvZmkkHWySpoFz+WOlVNLz3DQoC4ISUHSV9n5jMxPHtKGJ01F4uu2PsXBB8sSg==", + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/configstore/node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deeks": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/deeks/-/deeks-3.1.0.tgz", + "integrity": "sha512-e7oWH1LzIdv/prMQ7pmlDlaVoL64glqzvNgkgQNgyec9ORPHrT2jaOqMtRyqJuwWjtfb6v+2rk9pmaHj+F137A==", + "dev": true, + "engines": { + "node": ">= 16" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/doc-path": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-4.1.1.tgz", + "integrity": "sha512-h1ErTglQAVv2gCnOpD3sFS6uolDbOKHDU1BZq+Kl3npPqroU3dYL42lUgMfd5UimlwtRgp7C9dLGwqQ5D2HYgQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "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==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/eventid/-/eventid-2.0.1.tgz", + "integrity": "sha512-sPNTqiMokAvV048P2c9+foqVJzk49o6d4e0D/sq5jog3pw+4kBgyR0gaM1FM7Mx6Kzd9dztesh9oYz1LWWOpzw==", + "dependencies": { + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eventid/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/extract-zip/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==", + "dev": true + }, + "node_modules/farmhash-modern": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", + "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fast-xml-parser": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz", + "integrity": "sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/firebase-admin": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.0.2.tgz", + "integrity": "sha512-YWVpoN+tZVSRXF0qC0gojoF5bSqvBRbnBk8+xUtFiguM2L4vB7f0moAwV1VVWDDHvTnvQ68OyTMpdp6wKo/clw==", + "dependencies": { + "@fastify/busboy": "^3.0.0", + "@firebase/database-compat": "^2.0.0", + "@firebase/database-types": "^1.0.6", + "@types/node": "^22.8.7", + "farmhash-modern": "^1.1.0", + "google-auth-library": "^9.14.2", + "jsonwebtoken": "^9.0.0", + "jwks-rsa": "^3.1.0", + "node-forge": "^1.3.1", + "uuid": "^11.0.2" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@google-cloud/firestore": "^7.11.0", + "@google-cloud/storage": "^7.14.0" + } + }, + "node_modules/firebase-admin/node_modules/@types/node": { + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz", + "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/firebase-admin/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + }, + "node_modules/firebase-admin/node_modules/uuid": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/firebase-functions": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-6.3.0.tgz", + "integrity": "sha512-88dRx3dPYvlxIN64H1lJCFV7wGt0cFL0lmXKzVm+rfnkILC0Qt3rMXp3/J/ojf2StclBwJBu2QZ4MRbNYh7B7g==", + "peer": true, + "dependencies": { + "@types/cors": "^2.8.5", + "@types/express": "^4.17.21", + "cors": "^2.8.5", + "express": "^4.21.0", + "protobufjs": "^7.2.2" + }, + "bin": { + "firebase-functions": "lib/bin/firebase-functions.js" + }, + "engines": { + "node": ">=14.10.0" + }, + "peerDependencies": { + "firebase-admin": "^11.10.0 || ^12.0.0 || ^13.0.0" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formdata-node/node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, + "node_modules/front-matter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", + "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", + "dependencies": { + "js-yaml": "^3.13.1" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "dependencies": { + "gaxios": "^6.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/genkit": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-0.9.12.tgz", + "integrity": "sha512-m1VQE/yhuii0y1aGTnkoSesSXTNE25q1s7vv5YVgJWa/t2gOXuznZOoHTJ847f/3mKC7fgnV7xGI+t/+7wbe0g==", + "dependencies": { + "@genkit-ai/ai": "0.9.12", + "@genkit-ai/core": "0.9.12", + "@genkit-ai/dotprompt": "0.9.12", + "uuid": "^10.0.0" + } + }, + "node_modules/genkit-cli": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-0.9.12.tgz", + "integrity": "sha512-Qsk7TmVBxghTT+0cRe2j0fYJboUIfWhuUoeVbWshranye6tConGMyDEZdXBIeMWIixSbG7k8a7a2INdRdjcQJw==", + "dev": true, + "dependencies": { + "@genkit-ai/telemetry-server": "0.9.12", + "@genkit-ai/tools-common": "0.9.12", + "axios": "^1.7.7", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "extract-zip": "^2.0.1", + "get-port": "5.1.1", + "inquirer": "^8.2.0", + "open": "^6.3.0", + "ora": "^5.4.1" + }, + "bin": { + "genkit": "dist/bin/genkit.js" + } + }, + "node_modules/genkitx-chromadb": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/genkitx-chromadb/-/genkitx-chromadb-0.9.12.tgz", + "integrity": "sha512-dBQY7vvT3EdGhl7EHFZdlhXabqSi2pbtgsndyiFQb634vjbDIdyNggvqh9w89p2pmqlf+OymMJyahRoSa+3AGw==", + "dependencies": { + "chromadb": "1.8.1", + "ts-md5": "^1.3.1" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/genkitx-chromadb/node_modules/@google/generative-ai": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.1.3.tgz", + "integrity": "sha512-Cm4uJX1sKarpm1mje/MiOIinM7zdUUrQp/5/qGPAgznbdd/B9zup5ehT6c1qGqycFcSopTA1J1HpqHS5kJR8hQ==", + "optional": true, + "peer": true, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/genkitx-chromadb/node_modules/chromadb": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/chromadb/-/chromadb-1.8.1.tgz", + "integrity": "sha512-NpbYydbg4Uqt/9BXKgkZXn0fqpsh2Z1yjhkhKH+rcHMoq0pwI18BFSU2QU7Fk/ZypwGefW2AvqyE/3ZJIgy4QA==", + "dependencies": { + "cliui": "^8.0.1", + "isomorphic-fetch": "^3.0.0" + }, + "engines": { + "node": ">=14.17.0" + }, + "peerDependencies": { + "@google/generative-ai": "^0.1.1", + "cohere-ai": "^5.0.0 || ^6.0.0 || ^7.0.0", + "openai": "^3.0.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@google/generative-ai": { + "optional": true + }, + "cohere-ai": { + "optional": true + }, + "openai": { + "optional": true + } + } + }, + "node_modules/genkitx-ollama": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/genkitx-ollama/-/genkitx-ollama-0.9.12.tgz", + "integrity": "sha512-mxj+Amvl6NRYxmAnYhO6yVXmG0aO+Fb5XL9OnUJnmauzqXkYyaxeblVgLKQtVJAYlnStZ50iYjMpUCMfd11QTA==", + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/genkitx-pinecone": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/genkitx-pinecone/-/genkitx-pinecone-0.9.12.tgz", + "integrity": "sha512-T4vgRMplRWeKGTsDfOZ1e/W1Y1VozMfYUod4UzOj4DfAn/QoS4YwR5TmybD+IRCBvb6cOaoXJ3qCMmhnw7S9vw==", + "dependencies": { + "@pinecone-database/pinecone": "^2.0.1", + "ts-md5": "^1.3.1" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", + "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/google-gax/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/googleapis": { + "version": "140.0.1", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-140.0.1.tgz", + "integrity": "sha512-ZGvBX4mQcFXO9ACnVNg6Aqy3KtBPB5zTuue43YVLxwn8HSv8jB7w+uDKoIPSoWuxGROgnj2kbng6acXncOQRNA==", + "dependencies": { + "google-auth-library": "^9.0.0", + "googleapis-common": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis-common": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.2.0.tgz", + "integrity": "sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==", + "dependencies": { + "extend": "^3.0.2", + "gaxios": "^6.0.3", + "google-auth-library": "^9.7.0", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis-common/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz", + "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/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==" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/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==" + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/import-in-the-middle": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.12.0.tgz", + "integrity": "sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/isomorphic-fetch/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-2-csv": { + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.8.tgz", + "integrity": "sha512-eMQHOwV+av8Sgo+fkbEbQWOw/kwh89AZ5fNA8TYfcooG6TG1ZOL2WcPUrngIMIK8dBJitQ8QEU0zbncQ0CX4CQ==", + "dev": true, + "dependencies": { + "deeks": "3.1.0", + "doc-path": "4.1.1" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/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==" + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", + "dependencies": { + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", + "debug": "^4.3.4", + "jose": "^4.14.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jwks-rsa/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/jwks-rsa/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==" + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/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==" + }, + "node_modules/long": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-memoizer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", + "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "6.0.0" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openai": { + "version": "4.81.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.81.0.tgz", + "integrity": "sha512-lXkFkV+He3O6RGnldHncRGef4uWHssDsAVwN5I3bWcgIdDPy/w8vgtIAwvZxAj49m4WiwWVD0+eGTJ9xOv/ISA==", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.74", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", + "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/openapi3-ts": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.4.0.tgz", + "integrity": "sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==", + "dev": true, + "dependencies": { + "yaml": "^2.5.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "optional": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==" + }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz", + "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobuf.js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/protobuf.js/-/protobuf.js-1.1.2.tgz", + "integrity": "sha512-USO7Xus/pzPw549M1TguiyoOrKEhm9VMXv+CkDufcjMC8Rd7EPbxeRQPEjCV8ua1tm0k7z9xHkogcxovZogWdA==", + "dependencies": { + "long": "~1.1.2" + } + }, + "node_modules/protobuf.js/node_modules/long": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/long/-/long-1.1.5.tgz", + "integrity": "sha512-TU6nAF5SdasnTr28c7e74P4Crbn9o3/zwo1pM22Wvg2i2vlZ4Eelxwu4QT7j21z0sDBlJDEnEZjXTZg2J8WJrg==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", + "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", + "dependencies": { + "duplexify": "^4.1.1", + "inherits": "^2.0.3", + "pump": "^3.0.0" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.0.tgz", + "integrity": "sha512-/Tvpny/RVVicqlYTKwt/GtpZRsPG1CmJNhxVKGz+Sy/4MONfXCVNK69MFgGKdUt0/324q3ClI2dICcPgISrC8g==", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/require-in-the-middle/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/require-in-the-middle/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==" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/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==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "optional": true + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/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==" + }, + "node_modules/teeny-request/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/terminate": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/terminate/-/terminate-2.8.0.tgz", + "integrity": "sha512-bcbjJEg0wY5nuJXvGxxHfmoEPkyHLCctUKO6suwtxy7jVSgGcgPeGwpbLDLELFhIaxCGRr3dPvyNg1yuz2V0eg==", + "dev": true, + "dependencies": { + "ps-tree": "^1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ts-md5": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/ts-md5/-/ts-md5-1.3.1.tgz", + "integrity": "sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==", + "engines": { + "node": ">=12" + } + }, + "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==" + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" + }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dependencies": { + "inherits": "2.0.3" + } + }, + "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==" + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate.io-array": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", + "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==" + }, + "node_modules/validate.io-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz", + "integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", + "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} diff --git a/samples/js-menu/package-lock.json b/samples/js-menu/package-lock.json new file mode 100644 index 000000000..0143c09f7 --- /dev/null +++ b/samples/js-menu/package-lock.json @@ -0,0 +1,7763 @@ +{ + "name": "menu", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "menu", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@genkit-ai/dev-local-vectorstore": "^0.9.0-rc || ^0.9", + "@genkit-ai/evaluator": "^0.9.0-rc || ^0.9", + "@genkit-ai/firebase": "^0.9.0-rc || ^0.9", + "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9", + "genkit": "^0.9.0-rc || ^0.9" + }, + "devDependencies": { + "genkit-cli": "^0.9.0-rc || ^0.9", + "rimraf": "^6.0.1", + "typescript": "^5.3.3" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.24.3", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.24.3.tgz", + "integrity": "sha512-916wJXO6T6k8R6BAAcLhLPv/pnLGy7YSEBZXZ1XTFbLcTZE8oTy3oDW9WJf9KKZwMvVcePIfoTSvzXHRcGxkQQ==", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7", + "web-streams-polyfill": "^3.2.1" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/@types/node": { + "version": "18.19.74", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", + "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/@anthropic-ai/vertex-sdk": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@anthropic-ai/vertex-sdk/-/vertex-sdk-0.4.3.tgz", + "integrity": "sha512-2Uef0C5P2Hx+T88RnUSRA3u4aZqmqnrRSOb2N64ozgKPiSUPTM5JlggAq2b32yWMj5d3MLYa6spJXKMmHXOcoA==", + "dependencies": { + "@anthropic-ai/sdk": ">=0.14 <1", + "google-auth-library": "^9.4.2" + } + }, + "node_modules/@asteasolutions/zod-to-openapi": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.0.tgz", + "integrity": "sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==", + "dev": true, + "dependencies": { + "openapi3-ts": "^4.1.2" + }, + "peerDependencies": { + "zod": "^3.20.2" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@fastify/busboy": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", + "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==" + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==" + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==" + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==" + }, + "node_modules/@firebase/component": { + "version": "0.6.12", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.12.tgz", + "integrity": "sha512-YnxqjtohLbnb7raXt2YuA44cC1wA9GiehM/cmxrsoxKlFxBLy2V0OkRSj9gpngAE0UoJ421Wlav9ycO7lTPAUw==", + "dependencies": { + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.11.tgz", + "integrity": "sha512-gLrw/XeioswWUXgpVKCPAzzoOuvYNqK5fRUeiJTzO7Mlp9P6ylFEyPJlRBl1djqYye641r3MX6AmIeMXwjgwuQ==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.2.tgz", + "integrity": "sha512-5zvdnMsfDHvrQAVM6jBS7CkBpu+z3YbpFdhxRsrK1FP45IEfxlzpeuEUb17D/tpM10vfq4Ok0x5akIBaCv7gfA==", + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/database": "1.0.11", + "@firebase/database-types": "1.0.8", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.8.tgz", + "integrity": "sha512-6lPWIGeufhUq1heofZULyVvWFhD01TUrkkB9vyhmksjZ4XF7NaivQp9rICMk7QNhqwa+uDCaj4j+Q8qqcSVZ9g==", + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.10.3" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", + "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/util": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.3.tgz", + "integrity": "sha512-wfoF5LTy0m2ufUapV0ZnpcGQvuavTbJ5Qr1Ze9OJGL70cSMvhDyjS4w2121XdA3lGZSTOsDOyGhpoDtYwck85A==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@genkit-ai/ai": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-0.9.12.tgz", + "integrity": "sha512-xyVVAIGKNpj5zCkoEfWZkzwctl0/hmpX6vKZgdgMH2MiqP5LzTp7rUekBMon8c1rMDVAze97QVSjAmZIoMLSlA==", + "dependencies": { + "@genkit-ai/core": "0.9.12", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.11.19", + "colorette": "^2.0.20", + "json5": "^2.2.3", + "node-fetch": "^3.3.2", + "partial-json": "^0.1.7", + "uuid": "^10.0.0" + } + }, + "node_modules/@genkit-ai/core": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-0.9.12.tgz", + "integrity": "sha512-QPJZ3TL5Iq2fyeo30MpUjd3ZLcYQf97RsitDZhMbGy3vMwbgig0nhEbJ6v/qaWsOMqSfIxJE/gETY3mMts1vRg==", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "async-mutex": "^0.5.0", + "body-parser": "^1.20.3", + "cors": "^2.8.5", + "express": "^4.21.0", + "get-port": "^5.1.0", + "json-schema": "^0.4.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/dev-local-vectorstore": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/dev-local-vectorstore/-/dev-local-vectorstore-0.9.12.tgz", + "integrity": "sha512-wkrLRqTw1N50hZpQlC1Hq8yOWapyz3BiC03auK93j85sL2L6tIFQ1zFHw7qKD+t39fiTmpocl8O6spLEerUKoA==", + "dependencies": { + "compute-cosine-similarity": "^1.1.0", + "ts-md5": "^1.3.1" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/@genkit-ai/dotprompt": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/dotprompt/-/dotprompt-0.9.12.tgz", + "integrity": "sha512-eEHBRzRVemiPuqCBbXiLgltNWpmCHmC+gVHBhsAnrfOYBlwmPvh2nnAPBXGYnkDH87PuN11jg8YJQmO4kQuoSw==", + "dependencies": { + "@genkit-ai/ai": "0.9.12", + "@genkit-ai/core": "0.9.12", + "front-matter": "^4.0.2", + "handlebars": "^4.7.8", + "node-fetch": "^3.3.2" + } + }, + "node_modules/@genkit-ai/evaluator": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/evaluator/-/evaluator-0.9.12.tgz", + "integrity": "sha512-JbZfwwqDf6IP84cGt+QZlSB0h+FcdAM7Ur6A/lL5GJdMudIjRdxmhu3FEu/kWKK59EQlJShGBW5/Vyw93ynPqw==", + "dependencies": { + "@genkit-ai/dotprompt": "0.9.12", + "compute-cosine-similarity": "^1.1.0", + "node-fetch": "^3.3.2", + "path": "^0.12.7" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/@genkit-ai/firebase": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/firebase/-/firebase-0.9.12.tgz", + "integrity": "sha512-PbEAPO3GTENaJR5RUqrq88YIVxgkVi2pc82kOCrkJDpw/+PcyRIZcgvbQYSAzB932yVU0sy33zzdUfg9t2thMA==", + "dependencies": { + "@genkit-ai/google-cloud": "0.9.12", + "express": "^4.21.0", + "google-auth-library": "^9.6.3" + }, + "peerDependencies": { + "@google-cloud/firestore": "^7.6.0", + "firebase-admin": ">=12.2", + "firebase-functions": ">=4.8", + "genkit": "0.9.12" + } + }, + "node_modules/@genkit-ai/google-cloud": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/google-cloud/-/google-cloud-0.9.12.tgz", + "integrity": "sha512-hYDvdQmGdI/JC6hUa6hMs6+Kc04wmi+/54zJIB+EOiNLd6fJ193B+q/pHqI4UxvofJPsf9C6VTK5Q54+yyQm7w==", + "dependencies": { + "@google-cloud/logging-winston": "^6.0.0", + "@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.19.0", + "@google-cloud/opentelemetry-cloud-trace-exporter": "^2.4.1", + "@google-cloud/opentelemetry-resource-util": "^2.4.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/auto-instrumentations-node": "^0.49.1", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/instrumentation-pino": "^0.41.0", + "@opentelemetry/instrumentation-winston": "^0.39.0", + "@opentelemetry/resources": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "google-auth-library": "^9.6.3", + "node-fetch": "^3.3.2", + "winston": "^3.12.0" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/@genkit-ai/telemetry-server": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-0.9.12.tgz", + "integrity": "sha512-8w1O9LOUtMCGc8bx+Vcd5D68L8ctM6H544Vxnfvy8gx4JHV5dj7vQiI9YDgm65pgP15pytQzt1TcRtOtYoimFw==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@genkit-ai/tools-common": "0.9.12", + "@google-cloud/firestore": "^7.6.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "async-mutex": "^0.5.0", + "express": "^4.21.0", + "zod": "^3.22.4" + } + }, + "node_modules/@genkit-ai/tools-common": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-0.9.12.tgz", + "integrity": "sha512-A9ToM/CY6vxcBSw4o47Q7qrpMaRsk4P6ZuzoDMlCzCVI+RZN5ulTL4LXMrq6nQ/KxY1GT94bSoulROXBMzW/Gw==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@trpc/server": "10.45.0", + "adm-zip": "^0.5.12", + "axios": "^1.7.7", + "body-parser": "^1.20.2", + "chokidar": "^3.5.3", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "configstore": "^5.0.1", + "express": "^4.21.0", + "get-port": "5.1.1", + "glob": "^10.3.12", + "inquirer": "^8.2.0", + "js-yaml": "^4.1.0", + "json-2-csv": "^5.5.1", + "json-schema": "^0.4.0", + "terminate": "^2.6.1", + "tsx": "^4.19.2", + "uuid": "^9.0.1", + "winston": "^3.11.0", + "yaml": "^2.4.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/tools-common/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==", + "dev": true + }, + "node_modules/@genkit-ai/tools-common/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@genkit-ai/tools-common/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@genkit-ai/vertexai": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/vertexai/-/vertexai-0.9.12.tgz", + "integrity": "sha512-lA6lwh5jgYgFr7RsFCXs+iASUZI2CmrGQEJCR+/qKJTNG0OyI4O7CdoT5SZSc1fK7oXeWH0/4x6mIwcngjy2Gw==", + "dependencies": { + "@anthropic-ai/sdk": "^0.24.3", + "@anthropic-ai/vertex-sdk": "^0.4.0", + "@google-cloud/aiplatform": "^3.23.0", + "@google-cloud/vertexai": "^1.9.0", + "@mistralai/mistralai-gcp": "^1.3.5", + "google-auth-library": "^9.14.2", + "googleapis": "^140.0.1", + "node-fetch": "^3.3.2", + "openai": "^4.52.7" + }, + "optionalDependencies": { + "@google-cloud/bigquery": "^7.8.0", + "firebase-admin": ">=12.2" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/@google-cloud/aiplatform": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/@google-cloud/aiplatform/-/aiplatform-3.34.0.tgz", + "integrity": "sha512-Ii1CXJ59g5hcVYNZOx08XBV5nq0JIOSo2I9uC/WYkdXWekc3XSV9emRz8pKOQSULzrTOTnD80N4re49S07xfyQ==", + "dependencies": { + "google-gax": "^4.0.3", + "protobuf.js": "^1.1.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/bigquery": { + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@google-cloud/bigquery/-/bigquery-7.9.1.tgz", + "integrity": "sha512-ZkcRMpBoFLxIh6TiQBywA22yT3c2j0f07AHWEMjtYqMQzZQbFrpxuJU2COp3tyjZ91ZIGHe4gY7/dGZL88cltg==", + "optional": true, + "dependencies": { + "@google-cloud/common": "^5.0.0", + "@google-cloud/paginator": "^5.0.2", + "@google-cloud/precise-date": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "arrify": "^2.0.1", + "big.js": "^6.0.0", + "duplexify": "^4.0.0", + "extend": "^3.0.2", + "is": "^3.3.0", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/bigquery/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@google-cloud/common": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-5.0.2.tgz", + "integrity": "sha512-V7bmBKYQyu0eVG2BFejuUjlBt+zrya6vtsKdY+JxMM/dNntPF41vZ9+LhOshEUH01zOHEqBSvI7Dad7ZS6aUeA==", + "dependencies": { + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "arrify": "^2.0.1", + "duplexify": "^4.1.1", + "extend": "^3.0.2", + "google-auth-library": "^9.0.0", + "html-entities": "^2.5.2", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/firestore": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.0.tgz", + "integrity": "sha512-88uZ+jLsp1aVMj7gh3EKYH1aulTAMFAp8sH/v5a9w8q8iqSG27RiWLoxSAFr/XocZ9hGiWH1kEnBw+zl3xAgNA==", + "dependencies": { + "@opentelemetry/api": "^1.3.0", + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.3.3", + "protobufjs": "^7.2.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/logging": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@google-cloud/logging/-/logging-11.2.0.tgz", + "integrity": "sha512-Ma94jvuoMpbgNniwtelOt8w82hxK62FuOXZonEv0Hyk3B+/YVuLG/SWNyY9yMso/RXnPEc1fP2qo9kDrjf/b2w==", + "dependencies": { + "@google-cloud/common": "^5.0.0", + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "@opentelemetry/api": "^1.7.0", + "arrify": "^2.0.1", + "dot-prop": "^6.0.0", + "eventid": "^2.0.0", + "extend": "^3.0.2", + "gcp-metadata": "^6.0.0", + "google-auth-library": "^9.0.0", + "google-gax": "^4.0.3", + "on-finished": "^2.3.0", + "pumpify": "^2.0.1", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/logging-winston": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/logging-winston/-/logging-winston-6.0.0.tgz", + "integrity": "sha512-/lVp7CyT3nFOr+AjQlZnJhTIOf+kcNGB4JTziL0fkX6Ov/2qNKtRGS/NqE6cD+VSPiv5jLOty3LgkRsXMpYxQQ==", + "dependencies": { + "@google-cloud/logging": "^11.0.0", + "google-auth-library": "^9.0.0", + "lodash.mapvalues": "^4.6.0", + "winston-transport": "^4.3.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "winston": ">=3.2.1" + } + }, + "node_modules/@google-cloud/logging/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@google-cloud/opentelemetry-cloud-monitoring-exporter": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@google-cloud/opentelemetry-cloud-monitoring-exporter/-/opentelemetry-cloud-monitoring-exporter-0.19.0.tgz", + "integrity": "sha512-5SOPXwC6RET4ZvXxw5D97dp8fWpqWEunHrzrUUGXhG4UAeedQe1KvYV8CK+fnaAbN2l2ha6QDYspT6z40TVY0g==", + "dependencies": { + "@google-cloud/opentelemetry-resource-util": "^2.3.0", + "@google-cloud/precise-date": "^4.0.0", + "google-auth-library": "^9.0.0", + "googleapis": "^137.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/resources": "^1.0.0", + "@opentelemetry/sdk-metrics": "^1.0.0" + } + }, + "node_modules/@google-cloud/opentelemetry-cloud-monitoring-exporter/node_modules/googleapis": { + "version": "137.1.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-137.1.0.tgz", + "integrity": "sha512-2L7SzN0FLHyQtFmyIxrcXhgust77067pkkduqkbIpDuj9JzVnByxsRrcRfUMFQam3rQkWW2B0f1i40IwKDWIVQ==", + "dependencies": { + "google-auth-library": "^9.0.0", + "googleapis-common": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/opentelemetry-cloud-trace-exporter": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@google-cloud/opentelemetry-cloud-trace-exporter/-/opentelemetry-cloud-trace-exporter-2.4.1.tgz", + "integrity": "sha512-Dq2IyAyA9PCjbjLOn86i2byjkYPC59b5ic8k/L4q5bBWH0Jro8lzMs8C0G5pJfqh2druj8HF+oAIAlSdWQ+Z9Q==", + "dependencies": { + "@google-cloud/opentelemetry-resource-util": "^2.4.0", + "@grpc/grpc-js": "^1.1.8", + "@grpc/proto-loader": "^0.7.0", + "google-auth-library": "^9.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/resources": "^1.0.0", + "@opentelemetry/sdk-trace-base": "^1.0.0" + } + }, + "node_modules/@google-cloud/opentelemetry-resource-util": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@google-cloud/opentelemetry-resource-util/-/opentelemetry-resource-util-2.4.0.tgz", + "integrity": "sha512-/7ujlMoKtDtrbQlJihCjQnm31n2s2RTlvJqcSbt2jV3OkCzPAdo3u31Q13HNugqtIRUSk7bUoLx6AzhURkhW4w==", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.22.0", + "gcp-metadata": "^6.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/resources": "^1.0.0" + } + }, + "node_modules/@google-cloud/paginator": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", + "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/precise-date": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-4.0.0.tgz", + "integrity": "sha512-1TUx3KdaU3cN7nfCdNf+UVqA/PSX29Cjcox3fZZBtINlRrXVTmUkQnCKv2MbBUbCopbK4olAT1IHl76uZyCiVA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/projectify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", + "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/promisify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", + "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@google-cloud/storage": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.15.0.tgz", + "integrity": "sha512-/j/+8DFuEOo33fbdX0V5wjooOoFahEaMEdImHBmM2tH9MPHJYNtmXOf2sGUmZmiufSukmBEvdlzYgDkkgeBiVQ==", + "optional": true, + "dependencies": { + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "abort-controller": "^3.0.0", + "async-retry": "^1.3.3", + "duplexify": "^4.1.3", + "fast-xml-parser": "^4.4.1", + "gaxios": "^6.0.2", + "google-auth-library": "^9.6.3", + "html-entities": "^2.5.2", + "mime": "^3.0.0", + "p-limit": "^3.0.1", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google-cloud/storage/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@google-cloud/vertexai": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@google-cloud/vertexai/-/vertexai-1.9.2.tgz", + "integrity": "sha512-pJSUG3r5QIvCFNfkz7/y7kEqvEJaVAk0jZbZoKbcPCRUnXaUeAq7p8I0oklqetGyxbUcZ2FOGpt+Y+4uIltVPg==", + "dependencies": { + "google-auth-library": "^9.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz", + "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@mistralai/mistralai-gcp": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai-gcp/-/mistralai-gcp-1.4.0.tgz", + "integrity": "sha512-QYBbR/T1U4qZ88m6l5RpOlhly2mXWsXi0owicSX6zt6pBaMORxRs6ZRLmaYX5BNjGqxQUl+LtsSwpMtXW2uU2A==", + "dependencies": { + "google-auth-library": "^9.11.0" + }, + "peerDependencies": { + "zod": ">= 3" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz", + "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/auto-instrumentations-node": { + "version": "0.49.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.49.2.tgz", + "integrity": "sha512-xtETEPmAby/3MMmedv8Z/873sdLTWg+Vq98rtm4wbwvAiXBB/ao8qRyzRlvR2MR6puEr+vIB/CXeyJnzNA3cyw==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/instrumentation-amqplib": "^0.41.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.43.0", + "@opentelemetry/instrumentation-aws-sdk": "^0.43.1", + "@opentelemetry/instrumentation-bunyan": "^0.40.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.40.0", + "@opentelemetry/instrumentation-connect": "^0.38.0", + "@opentelemetry/instrumentation-cucumber": "^0.8.0", + "@opentelemetry/instrumentation-dataloader": "^0.11.0", + "@opentelemetry/instrumentation-dns": "^0.38.0", + "@opentelemetry/instrumentation-express": "^0.41.1", + "@opentelemetry/instrumentation-fastify": "^0.38.0", + "@opentelemetry/instrumentation-fs": "^0.14.0", + "@opentelemetry/instrumentation-generic-pool": "^0.38.1", + "@opentelemetry/instrumentation-graphql": "^0.42.0", + "@opentelemetry/instrumentation-grpc": "^0.52.0", + "@opentelemetry/instrumentation-hapi": "^0.40.0", + "@opentelemetry/instrumentation-http": "^0.52.0", + "@opentelemetry/instrumentation-ioredis": "^0.42.0", + "@opentelemetry/instrumentation-kafkajs": "^0.2.0", + "@opentelemetry/instrumentation-knex": "^0.39.0", + "@opentelemetry/instrumentation-koa": "^0.42.0", + "@opentelemetry/instrumentation-lru-memoizer": "^0.39.0", + "@opentelemetry/instrumentation-memcached": "^0.38.0", + "@opentelemetry/instrumentation-mongodb": "^0.46.0", + "@opentelemetry/instrumentation-mongoose": "^0.41.0", + "@opentelemetry/instrumentation-mysql": "^0.40.0", + "@opentelemetry/instrumentation-mysql2": "^0.40.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.39.0", + "@opentelemetry/instrumentation-net": "^0.38.0", + "@opentelemetry/instrumentation-pg": "^0.43.0", + "@opentelemetry/instrumentation-pino": "^0.41.0", + "@opentelemetry/instrumentation-redis": "^0.41.0", + "@opentelemetry/instrumentation-redis-4": "^0.41.1", + "@opentelemetry/instrumentation-restify": "^0.40.0", + "@opentelemetry/instrumentation-router": "^0.39.0", + "@opentelemetry/instrumentation-socket.io": "^0.41.0", + "@opentelemetry/instrumentation-tedious": "^0.13.0", + "@opentelemetry/instrumentation-undici": "^0.5.0", + "@opentelemetry/instrumentation-winston": "^0.39.0", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.29.0", + "@opentelemetry/resource-detector-aws": "^1.6.0", + "@opentelemetry/resource-detector-azure": "^0.2.10", + "@opentelemetry/resource-detector-container": "^0.4.0", + "@opentelemetry/resource-detector-gcp": "^0.29.10", + "@opentelemetry/resources": "^1.24.0", + "@opentelemetry/sdk-node": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.4.1" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz", + "integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", + "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz", + "integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz", + "integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-amqplib": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.41.0.tgz", + "integrity": "sha512-00Oi6N20BxJVcqETjgNzCmVKN+I5bJH/61IlHiIWd00snj1FdgiIKlpE4hYVacTB2sjIBB3nTbHskttdZEE2eg==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-lambda": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.43.0.tgz", + "integrity": "sha512-pSxcWlsE/pCWQRIw92sV2C+LmKXelYkjkA7C5s39iPUi4pZ2lA1nIiw+1R/y2pDEhUHcaKkNyljQr3cx9ZpVlQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/propagator-aws-xray": "^1.3.1", + "@opentelemetry/resources": "^1.8.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "@types/aws-lambda": "8.10.122" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-aws-sdk": { + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.43.1.tgz", + "integrity": "sha512-qLT2cCniJ5W+6PFzKbksnoIQuq9pS83nmgaExfUwXVvlwi0ILc50dea0tWBHZMkdIDa/zZdcuFrJ7+fUcSnRow==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/propagation-utils": "^0.30.10", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-bunyan": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.40.0.tgz", + "integrity": "sha512-aZ4cXaGWwj79ZXSYrgFVsrDlE4mmf2wfvP9bViwRc0j75A6eN6GaHYHqufFGMTCqASQn5pIjjP+Bx+PWTGiofw==", + "dependencies": { + "@opentelemetry/api-logs": "^0.52.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@types/bunyan": "1.8.9" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cassandra-driver": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.40.0.tgz", + "integrity": "sha512-JxbM39JU7HxE9MTKKwi6y5Z3mokjZB2BjwfqYi4B3Y29YO3I42Z7eopG6qq06yWZc+nQli386UDQe0d9xKmw0A==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-connect": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.38.0.tgz", + "integrity": "sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "@types/connect": "3.4.36" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-cucumber": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.8.0.tgz", + "integrity": "sha512-ieTm4RBIlZt2brPwtX5aEZYtYnkyqhAVXJI9RIohiBVMe5DxiwCwt+2Exep/nDVqGPX8zRBZUl4AEw423OxJig==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dataloader": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.11.0.tgz", + "integrity": "sha512-27urJmwkH4KDaMJtEv1uy2S7Apk4XbN4AgWMdfMJbi7DnOduJmeuA+DpJCwXB72tEWXo89z5T3hUVJIDiSNmNw==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-dns": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.38.0.tgz", + "integrity": "sha512-Um07I0TQXDWa+ZbEAKDFUxFH40dLtejtExDOMLNJ1CL8VmOmA71qx93Qi/QG4tGkiI1XWqr7gF/oiMCJ4m8buQ==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-express": { + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.41.1.tgz", + "integrity": "sha512-uRx0V3LPGzjn2bxAnV8eUsDT82vT7NTwI0ezEuPMBOTOsnPpGhWdhcdNdhH80sM4TrWrOfXm9HGEdfWE3TRIww==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fastify": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.38.0.tgz", + "integrity": "sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-fs": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.14.0.tgz", + "integrity": "sha512-pVc8P5AgliC1DphyyBUgsxXlm2XaPH4BpYvt7rAZDMIqUpRk8gs19SioABtKqqxvFzg5jPtgJfJsdxq0Y+maLw==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-generic-pool": { + "version": "0.38.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.38.1.tgz", + "integrity": "sha512-WvssuKCuavu/hlq661u82UWkc248cyI/sT+c2dEIj6yCk0BUkErY1D+9XOO+PmHdJNE+76i2NdcvQX5rJrOe/w==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-graphql": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.42.0.tgz", + "integrity": "sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.52.1.tgz", + "integrity": "sha512-EdSDiDSAO+XRXk/ZN128qQpBo1I51+Uay/LUPcPQhSRGf7fBPIEUBeOLQiItguGsug5MGOYjql2w/1wCQF3fdQ==", + "dependencies": { + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-hapi": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.40.0.tgz", + "integrity": "sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.52.1.tgz", + "integrity": "sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/semantic-conventions": "1.25.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation-ioredis": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.42.0.tgz", + "integrity": "sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.23.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-kafkajs": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.2.0.tgz", + "integrity": "sha512-uKKmhEFd0zR280tJovuiBG7cfnNZT4kvVTvqtHPxQP7nOmRbJstCYHFH13YzjVcKjkmoArmxiSulmZmF7SLIlg==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.24.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-knex": { + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.39.0.tgz", + "integrity": "sha512-lRwTqIKQecPWDkH1KEcAUcFhCaNssbKSpxf4sxRTAROCwrCEnYkjOuqJHV+q1/CApjMTaKu0Er4LBv/6bDpoxA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-koa": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.42.0.tgz", + "integrity": "sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-lru-memoizer": { + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.39.0.tgz", + "integrity": "sha512-eU1Wx1RRTR/2wYXFzH9gcpB8EPmhYlNDIUHzUXjyUE0CAXEJhBLkYNlzdaVCoQDw2neDqS+Woshiia6+emWK9A==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-memcached": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.38.0.tgz", + "integrity": "sha512-tPmyqQEZNyrvg6G+iItdlguQEcGzfE+bJkpQifmBXmWBnoS5oU3UxqtyYuXGL2zI9qQM5yMBHH4nRXWALzy7WA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.23.0", + "@types/memcached": "^2.2.6" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.46.0.tgz", + "integrity": "sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/sdk-metrics": "^1.9.1", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mongoose": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.41.0.tgz", + "integrity": "sha512-ivJg4QnnabFxxoI7K8D+in7hfikjte38sYzJB9v1641xJk9Esa7jM3hmbPB7lxwcgWJLVEDvfPwobt1if0tXxA==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.40.0.tgz", + "integrity": "sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "@types/mysql": "2.15.22" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-mysql2": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.40.0.tgz", + "integrity": "sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "@opentelemetry/sql-common": "^0.40.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-nestjs-core": { + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.39.0.tgz", + "integrity": "sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.23.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-net": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.38.0.tgz", + "integrity": "sha512-stjow1PijcmUquSmRD/fSihm/H61DbjPlJuJhWUe7P22LFPjFhsrSeiB5vGj3vn+QGceNAs+kioUTzMGPbNxtg==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.23.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pg": { + "version": "0.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.43.0.tgz", + "integrity": "sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "@opentelemetry/sql-common": "^0.40.1", + "@types/pg": "8.6.1", + "@types/pg-pool": "2.0.4" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-pino": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.41.0.tgz", + "integrity": "sha512-Kpv0fJRk/8iMzMk5Ue5BsUJfHkBJ2wQoIi/qduU1a1Wjx9GLj6J2G17PHjPK5mnZjPNzkFOXFADZMfgDioliQw==", + "dependencies": { + "@opentelemetry/api-logs": "^0.52.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.41.0.tgz", + "integrity": "sha512-RJ1pwI3btykp67ts+5qZbaFSAAzacucwBet5/5EsKYtWBpHbWwV/qbGN/kIBzXg5WEZBhXLrR/RUq0EpEUpL3A==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-redis-4": { + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.41.1.tgz", + "integrity": "sha512-UqJAbxraBk7s7pQTlFi5ND4sAUs4r/Ai7gsAVZTQDbHl2kSsOp7gpHcpIuN5dpcI2xnuhM2tkH4SmEhbrv2S6Q==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/redis-common": "^0.36.2", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-restify": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.40.0.tgz", + "integrity": "sha512-sm/rH/GysY/KOEvZqYBZSLYFeXlBkHCgqPDgWc07tz+bHCN6mPs9P3otGOSTe7o3KAIM8Nc6ncCO59vL+jb2cA==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-router": { + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.39.0.tgz", + "integrity": "sha512-LaXnVmD69WPC4hNeLzKexCCS19hRLrUw3xicneAMkzJSzNJvPyk7G6I7lz7VjQh1cooObPBt9gNyd3hhTCUrag==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-socket.io": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.41.0.tgz", + "integrity": "sha512-7fzDe9/FpO6NFizC/wnzXXX7bF9oRchsD//wFqy5g5hVEgXZCQ70IhxjrKdBvgjyIejR9T9zTvfQ6PfVKfkCAw==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-tedious": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.13.0.tgz", + "integrity": "sha512-Pob0+0R62AqXH50pjazTeGBy/1+SK4CYpFUBV5t7xpbpeuQezkkgVGvLca84QqjBqQizcXedjpUJLgHQDixPQg==", + "dependencies": { + "@opentelemetry/instrumentation": "^0.52.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "@types/tedious": "^4.0.14" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.5.0.tgz", + "integrity": "sha512-aNTeSrFAVcM9qco5DfZ9DNXu6hpMRe8Kt8nCDHfMWDB3pwgGVUE76jTdohc+H/7eLRqh4L7jqs5NSQoHw7S6ww==", + "dependencies": { + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.7.0" + } + }, + "node_modules/@opentelemetry/instrumentation-winston": { + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.39.0.tgz", + "integrity": "sha512-v/1xziLJ9CyB3YDjBSBzbB70Qd0JwWTo36EqWK5m3AR0CzsyMQQmf3ZIZM6sgx7hXMcRQ0pnEYhg6nhrUQPm9A==", + "dependencies": { + "@opentelemetry/api-logs": "^0.52.0", + "@opentelemetry/instrumentation": "^0.52.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz", + "integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagation-utils": { + "version": "0.30.15", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagation-utils/-/propagation-utils-0.30.15.tgz", + "integrity": "sha512-nQ30K+eXTkd9Kt8yep9FPrqogS712GvdkV6R1T+xZMSZnFrRCyZuWxMtP3+s3hrK2HWw3ti4lsIfBzsHWYiyrA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/propagator-aws-xray": { + "version": "1.26.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-1.26.1.tgz", + "integrity": "sha512-rMffLq+rPJrejFPv40MQGXFroAfyg9zfVzO0Jy4wbfEkSJ4uOIeC8T2aAf7Z5igZ7nPZsQJ7xLpbCSq8MnQziQ==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", + "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", + "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/redis-common": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz", + "integrity": "sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { + "version": "0.29.7", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.29.7.tgz", + "integrity": "sha512-PExUl/R+reSQI6Y/eNtgAsk6RHk1ElYSzOa8/FHfdc/nLmx9sqMasBEpLMkETkzDP7t27ORuXe4F9vwkV2uwwg==", + "dependencies": { + "@opentelemetry/core": "^1.26.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-aws": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-1.11.0.tgz", + "integrity": "sha512-j7qQ75enAJrlSPkPowasScuukZ2ffFG659rhxOpUM4dBe/O8Jpq+dy4pIdFtjWKkM9i7LgisdUt/GW7wGIWoEQ==", + "dependencies": { + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-azure": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.2.12.tgz", + "integrity": "sha512-iIarQu6MiCjEEp8dOzmBvCSlRITPFTinFB2oNKAjU6xhx8d7eUcjNOKhBGQTvuCriZrxrEvDaEEY9NfrPQ6uYQ==", + "dependencies": { + "@opentelemetry/core": "^1.25.1", + "@opentelemetry/resources": "^1.10.1", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-container": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.4.4.tgz", + "integrity": "sha512-ZEN2mq7lIjQWJ8NTt1umtr6oT/Kb89856BOmESLSvgSHbIwOFYs7cSfSRH5bfiVw6dXTQAVbZA/wLgCHKrebJA==", + "dependencies": { + "@opentelemetry/core": "^1.26.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resource-detector-gcp": { + "version": "0.29.13", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.29.13.tgz", + "integrity": "sha512-vdotx+l3Q+89PeyXMgKEGnZ/CwzwMtuMi/ddgD9/5tKZ08DfDGB2Npz9m2oXPHRCjc4Ro6ifMqFlRyzIvgOjhg==", + "dependencies": { + "@opentelemetry/core": "^1.0.0", + "@opentelemetry/resources": "^1.10.0", + "@opentelemetry/semantic-conventions": "^1.27.0", + "gcp-metadata": "^6.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz", + "integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", + "@opentelemetry/exporter-zipkin": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/sdk-trace-node": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", + "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.25.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/propagator-b3": "1.25.1", + "@opentelemetry/propagator-jaeger": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sql-common": { + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz", + "integrity": "sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==", + "dependencies": { + "@opentelemetry/core": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trpc/server": { + "version": "10.45.0", + "resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.45.0.tgz", + "integrity": "sha512-2Fwzv6nqpE0Ie/G7PeS0EVR89zLm+c1Mw7T+RAGtU807j4oaUx0zGkBXTu5u9AI+j+BYNN2GZxJcuDTAecbr1A==", + "dev": true, + "funding": [ + "https://trpc.io/sponsor" + ] + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.122", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.122.tgz", + "integrity": "sha512-vBkIh9AY22kVOCEKo5CJlyCgmSWvasC+SWUxL/x/vOwRobMpI/HG1xp/Ae3AqmSiZeLUbOhW0FCD3ZjqqUxmXw==" + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bunyan": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.9.tgz", + "integrity": "sha512-ZqS9JGpBxVOvsawzmVt30sP++gSQMTejCkIAQ3VdadOcRE8izTyW66hufvwLeH+YEGP6Js2AW7Gz+RMyvrEbmw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==" + }, + "node_modules/@types/connect": { + "version": "3.4.36", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", + "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.8.tgz", + "integrity": "sha512-7fx54m60nLFUVYlxAB1xpe9CBWX2vSrk50Y6ogRJ1v5xxtba7qXTg5BgYDN5dq+yuQQ9HaVlHJyAAt1/mxryFg==", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, + "node_modules/@types/memcached": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.10.tgz", + "integrity": "sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + }, + "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==" + }, + "node_modules/@types/mysql": { + "version": "2.15.22", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.22.tgz", + "integrity": "sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.17.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", + "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/pg": { + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.1.tgz", + "integrity": "sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/pg-pool": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.4.tgz", + "integrity": "sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==", + "dependencies": { + "@types/pg": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==" + }, + "node_modules/@types/tedious": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", + "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "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==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "optional": true, + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/big.js": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.2.tgz", + "integrity": "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==", + "optional": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bigjs" + } + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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==", + "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==" + }, + "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==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/compute-cosine-similarity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-cosine-similarity/-/compute-cosine-similarity-1.1.0.tgz", + "integrity": "sha512-FXhNx0ILLjGi9Z9+lglLzM12+0uoTnYkHm7GiadXDAr0HGVLm25OivUS1B/LPkbzzvlcXz/1EvWg9ZYyJSdhTw==", + "dependencies": { + "compute-dot": "^1.1.0", + "compute-l2norm": "^1.1.0", + "validate.io-array": "^1.0.5", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/compute-dot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-dot/-/compute-dot-1.1.0.tgz", + "integrity": "sha512-L5Ocet4DdMrXboss13K59OK23GXjiSia7+7Ukc7q4Bl+RVpIXK2W9IHMbWDZkh+JUEvJAwOKRaJDiFUa1LTnJg==", + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/compute-l2norm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/compute-l2norm/-/compute-l2norm-1.1.0.tgz", + "integrity": "sha512-6EHh1Elj90eU28SXi+h2PLnTQvZmkkHWySpoFz+WOlVNLz3DQoC4ISUHSV9n5jMxPHtKGJ01F4uu2PsXBB8sSg==", + "dependencies": { + "validate.io-array": "^1.0.3", + "validate.io-function": "^1.0.2" + } + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/configstore/node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deeks": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/deeks/-/deeks-3.1.0.tgz", + "integrity": "sha512-e7oWH1LzIdv/prMQ7pmlDlaVoL64glqzvNgkgQNgyec9ORPHrT2jaOqMtRyqJuwWjtfb6v+2rk9pmaHj+F137A==", + "dev": true, + "engines": { + "node": ">= 16" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/doc-path": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-4.1.1.tgz", + "integrity": "sha512-h1ErTglQAVv2gCnOpD3sFS6uolDbOKHDU1BZq+Kl3npPqroU3dYL42lUgMfd5UimlwtRgp7C9dLGwqQ5D2HYgQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "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==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/eventid/-/eventid-2.0.1.tgz", + "integrity": "sha512-sPNTqiMokAvV048P2c9+foqVJzk49o6d4e0D/sq5jog3pw+4kBgyR0gaM1FM7Mx6Kzd9dztesh9oYz1LWWOpzw==", + "dependencies": { + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eventid/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/extract-zip/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==", + "dev": true + }, + "node_modules/farmhash-modern": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", + "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fast-xml-parser": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz", + "integrity": "sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/firebase-admin": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.0.2.tgz", + "integrity": "sha512-YWVpoN+tZVSRXF0qC0gojoF5bSqvBRbnBk8+xUtFiguM2L4vB7f0moAwV1VVWDDHvTnvQ68OyTMpdp6wKo/clw==", + "dependencies": { + "@fastify/busboy": "^3.0.0", + "@firebase/database-compat": "^2.0.0", + "@firebase/database-types": "^1.0.6", + "@types/node": "^22.8.7", + "farmhash-modern": "^1.1.0", + "google-auth-library": "^9.14.2", + "jsonwebtoken": "^9.0.0", + "jwks-rsa": "^3.1.0", + "node-forge": "^1.3.1", + "uuid": "^11.0.2" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@google-cloud/firestore": "^7.11.0", + "@google-cloud/storage": "^7.14.0" + } + }, + "node_modules/firebase-admin/node_modules/@types/node": { + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz", + "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/firebase-admin/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + }, + "node_modules/firebase-admin/node_modules/uuid": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/firebase-functions": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-6.3.0.tgz", + "integrity": "sha512-88dRx3dPYvlxIN64H1lJCFV7wGt0cFL0lmXKzVm+rfnkILC0Qt3rMXp3/J/ojf2StclBwJBu2QZ4MRbNYh7B7g==", + "peer": true, + "dependencies": { + "@types/cors": "^2.8.5", + "@types/express": "^4.17.21", + "cors": "^2.8.5", + "express": "^4.21.0", + "protobufjs": "^7.2.2" + }, + "bin": { + "firebase-functions": "lib/bin/firebase-functions.js" + }, + "engines": { + "node": ">=14.10.0" + }, + "peerDependencies": { + "firebase-admin": "^11.10.0 || ^12.0.0 || ^13.0.0" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formdata-node/node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, + "node_modules/front-matter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", + "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", + "dependencies": { + "js-yaml": "^3.13.1" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "dependencies": { + "gaxios": "^6.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/genkit": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-0.9.12.tgz", + "integrity": "sha512-m1VQE/yhuii0y1aGTnkoSesSXTNE25q1s7vv5YVgJWa/t2gOXuznZOoHTJ847f/3mKC7fgnV7xGI+t/+7wbe0g==", + "dependencies": { + "@genkit-ai/ai": "0.9.12", + "@genkit-ai/core": "0.9.12", + "@genkit-ai/dotprompt": "0.9.12", + "uuid": "^10.0.0" + } + }, + "node_modules/genkit-cli": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-0.9.12.tgz", + "integrity": "sha512-Qsk7TmVBxghTT+0cRe2j0fYJboUIfWhuUoeVbWshranye6tConGMyDEZdXBIeMWIixSbG7k8a7a2INdRdjcQJw==", + "dev": true, + "dependencies": { + "@genkit-ai/telemetry-server": "0.9.12", + "@genkit-ai/tools-common": "0.9.12", + "axios": "^1.7.7", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "extract-zip": "^2.0.1", + "get-port": "5.1.1", + "inquirer": "^8.2.0", + "open": "^6.3.0", + "ora": "^5.4.1" + }, + "bin": { + "genkit": "dist/bin/genkit.js" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", + "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/google-gax/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/googleapis": { + "version": "140.0.1", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-140.0.1.tgz", + "integrity": "sha512-ZGvBX4mQcFXO9ACnVNg6Aqy3KtBPB5zTuue43YVLxwn8HSv8jB7w+uDKoIPSoWuxGROgnj2kbng6acXncOQRNA==", + "dependencies": { + "google-auth-library": "^9.0.0", + "googleapis-common": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis-common": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.2.0.tgz", + "integrity": "sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==", + "dependencies": { + "extend": "^3.0.2", + "gaxios": "^6.0.3", + "google-auth-library": "^9.7.0", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis-common/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz", + "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/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==" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/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==" + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/import-in-the-middle": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.12.0.tgz", + "integrity": "sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-2-csv": { + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.8.tgz", + "integrity": "sha512-eMQHOwV+av8Sgo+fkbEbQWOw/kwh89AZ5fNA8TYfcooG6TG1ZOL2WcPUrngIMIK8dBJitQ8QEU0zbncQ0CX4CQ==", + "dev": true, + "dependencies": { + "deeks": "3.1.0", + "doc-path": "4.1.1" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/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==" + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", + "dependencies": { + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", + "debug": "^4.3.4", + "jose": "^4.14.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jwks-rsa/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/jwks-rsa/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==" + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/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==" + }, + "node_modules/long": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-memoizer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", + "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "6.0.0" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openai": { + "version": "4.81.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.81.0.tgz", + "integrity": "sha512-lXkFkV+He3O6RGnldHncRGef4uWHssDsAVwN5I3bWcgIdDPy/w8vgtIAwvZxAj49m4WiwWVD0+eGTJ9xOv/ISA==", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.74", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", + "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/openapi3-ts": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.4.0.tgz", + "integrity": "sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==", + "dev": true, + "dependencies": { + "yaml": "^2.5.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "optional": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==" + }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz", + "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobuf.js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/protobuf.js/-/protobuf.js-1.1.2.tgz", + "integrity": "sha512-USO7Xus/pzPw549M1TguiyoOrKEhm9VMXv+CkDufcjMC8Rd7EPbxeRQPEjCV8ua1tm0k7z9xHkogcxovZogWdA==", + "dependencies": { + "long": "~1.1.2" + } + }, + "node_modules/protobuf.js/node_modules/long": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/long/-/long-1.1.5.tgz", + "integrity": "sha512-TU6nAF5SdasnTr28c7e74P4Crbn9o3/zwo1pM22Wvg2i2vlZ4Eelxwu4QT7j21z0sDBlJDEnEZjXTZg2J8WJrg==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", + "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", + "dependencies": { + "duplexify": "^4.1.1", + "inherits": "^2.0.3", + "pump": "^3.0.0" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.0.tgz", + "integrity": "sha512-/Tvpny/RVVicqlYTKwt/GtpZRsPG1CmJNhxVKGz+Sy/4MONfXCVNK69MFgGKdUt0/324q3ClI2dICcPgISrC8g==", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/require-in-the-middle/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/require-in-the-middle/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==" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/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==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "optional": true + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/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==" + }, + "node_modules/teeny-request/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/terminate": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/terminate/-/terminate-2.8.0.tgz", + "integrity": "sha512-bcbjJEg0wY5nuJXvGxxHfmoEPkyHLCctUKO6suwtxy7jVSgGcgPeGwpbLDLELFhIaxCGRr3dPvyNg1yuz2V0eg==", + "dev": true, + "dependencies": { + "ps-tree": "^1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ts-md5": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/ts-md5/-/ts-md5-1.3.1.tgz", + "integrity": "sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==", + "engines": { + "node": ">=12" + } + }, + "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==" + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" + }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dependencies": { + "inherits": "2.0.3" + } + }, + "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==" + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate.io-array": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", + "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==" + }, + "node_modules/validate.io-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz", + "integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", + "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} diff --git a/samples/js-schoolAgent/package-lock.json b/samples/js-schoolAgent/package-lock.json new file mode 100644 index 000000000..879ce12f7 --- /dev/null +++ b/samples/js-schoolAgent/package-lock.json @@ -0,0 +1,5622 @@ +{ + "name": "school-agent", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "school-agent", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@genkit-ai/googleai": "^0.9.3", + "genkit": "^0.9.3", + "google-auth-library": "^9.6.3", + "llm-chunk": "^0.0.1", + "pdf-parse": "^1.1.1" + }, + "devDependencies": { + "@types/pdf-parse": "^1.1.4", + "cross-env": "^7.0.3", + "genkit-cli": "^0.9.3", + "rimraf": "^6.0.1", + "tsx": "^4.19.1", + "typescript": "^5.3.3" + } + }, + "node_modules/@asteasolutions/zod-to-openapi": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.0.tgz", + "integrity": "sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==", + "dev": true, + "dependencies": { + "openapi3-ts": "^4.1.2" + }, + "peerDependencies": { + "zod": "^3.20.2" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@genkit-ai/ai": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-0.9.12.tgz", + "integrity": "sha512-xyVVAIGKNpj5zCkoEfWZkzwctl0/hmpX6vKZgdgMH2MiqP5LzTp7rUekBMon8c1rMDVAze97QVSjAmZIoMLSlA==", + "dependencies": { + "@genkit-ai/core": "0.9.12", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.11.19", + "colorette": "^2.0.20", + "json5": "^2.2.3", + "node-fetch": "^3.3.2", + "partial-json": "^0.1.7", + "uuid": "^10.0.0" + } + }, + "node_modules/@genkit-ai/core": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-0.9.12.tgz", + "integrity": "sha512-QPJZ3TL5Iq2fyeo30MpUjd3ZLcYQf97RsitDZhMbGy3vMwbgig0nhEbJ6v/qaWsOMqSfIxJE/gETY3mMts1vRg==", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "async-mutex": "^0.5.0", + "body-parser": "^1.20.3", + "cors": "^2.8.5", + "express": "^4.21.0", + "get-port": "^5.1.0", + "json-schema": "^0.4.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/dotprompt": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/dotprompt/-/dotprompt-0.9.12.tgz", + "integrity": "sha512-eEHBRzRVemiPuqCBbXiLgltNWpmCHmC+gVHBhsAnrfOYBlwmPvh2nnAPBXGYnkDH87PuN11jg8YJQmO4kQuoSw==", + "dependencies": { + "@genkit-ai/ai": "0.9.12", + "@genkit-ai/core": "0.9.12", + "front-matter": "^4.0.2", + "handlebars": "^4.7.8", + "node-fetch": "^3.3.2" + } + }, + "node_modules/@genkit-ai/googleai": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-0.9.12.tgz", + "integrity": "sha512-q6bX9Nq4xVpzH4vd9W+rg4xO7SVodG516K4BVM04DtxU5w0ogwLl/mRPEU0VIoISjD6FwaC4sPjPkEerTp8a4g==", + "dependencies": { + "@google/generative-ai": "^0.21.0", + "google-auth-library": "^9.6.3", + "node-fetch": "^3.3.2" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/@genkit-ai/telemetry-server": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-0.9.12.tgz", + "integrity": "sha512-8w1O9LOUtMCGc8bx+Vcd5D68L8ctM6H544Vxnfvy8gx4JHV5dj7vQiI9YDgm65pgP15pytQzt1TcRtOtYoimFw==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@genkit-ai/tools-common": "0.9.12", + "@google-cloud/firestore": "^7.6.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "async-mutex": "^0.5.0", + "express": "^4.21.0", + "zod": "^3.22.4" + } + }, + "node_modules/@genkit-ai/tools-common": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-0.9.12.tgz", + "integrity": "sha512-A9ToM/CY6vxcBSw4o47Q7qrpMaRsk4P6ZuzoDMlCzCVI+RZN5ulTL4LXMrq6nQ/KxY1GT94bSoulROXBMzW/Gw==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@trpc/server": "10.45.0", + "adm-zip": "^0.5.12", + "axios": "^1.7.7", + "body-parser": "^1.20.2", + "chokidar": "^3.5.3", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "configstore": "^5.0.1", + "express": "^4.21.0", + "get-port": "5.1.1", + "glob": "^10.3.12", + "inquirer": "^8.2.0", + "js-yaml": "^4.1.0", + "json-2-csv": "^5.5.1", + "json-schema": "^0.4.0", + "terminate": "^2.6.1", + "tsx": "^4.19.2", + "uuid": "^9.0.1", + "winston": "^3.11.0", + "yaml": "^2.4.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/tools-common/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==", + "dev": true + }, + "node_modules/@genkit-ai/tools-common/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@genkit-ai/tools-common/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@google-cloud/firestore": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.0.tgz", + "integrity": "sha512-88uZ+jLsp1aVMj7gh3EKYH1aulTAMFAp8sH/v5a9w8q8iqSG27RiWLoxSAFr/XocZ9hGiWH1kEnBw+zl3xAgNA==", + "dev": true, + "dependencies": { + "@opentelemetry/api": "^1.3.0", + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.3.3", + "protobufjs": "^7.2.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google/generative-ai": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz", + "integrity": "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz", + "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz", + "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz", + "integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", + "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz", + "integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz", + "integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz", + "integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", + "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", + "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz", + "integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", + "@opentelemetry/exporter-zipkin": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/sdk-trace-node": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", + "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.25.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/propagator-b3": "1.25.1", + "@opentelemetry/propagator-jaeger": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trpc/server": { + "version": "10.45.0", + "resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.45.0.tgz", + "integrity": "sha512-2Fwzv6nqpE0Ie/G7PeS0EVR89zLm+c1Mw7T+RAGtU807j4oaUx0zGkBXTu5u9AI+j+BYNN2GZxJcuDTAecbr1A==", + "dev": true, + "funding": [ + "https://trpc.io/sponsor" + ] + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "dev": true + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.17.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", + "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/pdf-parse": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/pdf-parse/-/pdf-parse-1.1.4.tgz", + "integrity": "sha512-+gbBHbNCVGGYw1S9lAIIvrHW47UYOhMIFUsJcMkMrzy1Jf0vulBN3XQIjPgnoOXveMuHnF3b57fXROnY/Or7eg==", + "dev": true + }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "dev": true, + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "dev": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "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==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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==", + "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==" + }, + "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==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deeks": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/deeks/-/deeks-3.1.0.tgz", + "integrity": "sha512-e7oWH1LzIdv/prMQ7pmlDlaVoL64glqzvNgkgQNgyec9ORPHrT2jaOqMtRyqJuwWjtfb6v+2rk9pmaHj+F137A==", + "dev": true, + "engines": { + "node": ">= 16" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/doc-path": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-4.1.1.tgz", + "integrity": "sha512-h1ErTglQAVv2gCnOpD3sFS6uolDbOKHDU1BZq+Kl3npPqroU3dYL42lUgMfd5UimlwtRgp7C9dLGwqQ5D2HYgQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "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==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/extract-zip/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==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, + "node_modules/front-matter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", + "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", + "dependencies": { + "js-yaml": "^3.13.1" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "dependencies": { + "gaxios": "^6.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/genkit": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-0.9.12.tgz", + "integrity": "sha512-m1VQE/yhuii0y1aGTnkoSesSXTNE25q1s7vv5YVgJWa/t2gOXuznZOoHTJ847f/3mKC7fgnV7xGI+t/+7wbe0g==", + "dependencies": { + "@genkit-ai/ai": "0.9.12", + "@genkit-ai/core": "0.9.12", + "@genkit-ai/dotprompt": "0.9.12", + "uuid": "^10.0.0" + } + }, + "node_modules/genkit-cli": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-0.9.12.tgz", + "integrity": "sha512-Qsk7TmVBxghTT+0cRe2j0fYJboUIfWhuUoeVbWshranye6tConGMyDEZdXBIeMWIixSbG7k8a7a2INdRdjcQJw==", + "dev": true, + "dependencies": { + "@genkit-ai/telemetry-server": "0.9.12", + "@genkit-ai/tools-common": "0.9.12", + "axios": "^1.7.7", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "extract-zip": "^2.0.1", + "get-port": "5.1.1", + "inquirer": "^8.2.0", + "open": "^6.3.0", + "ora": "^5.4.1" + }, + "bin": { + "genkit": "dist/bin/genkit.js" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", + "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", + "dev": true, + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/google-gax/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/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==", + "dev": true + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/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==" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/import-in-the-middle": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.12.0.tgz", + "integrity": "sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-2-csv": { + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.8.tgz", + "integrity": "sha512-eMQHOwV+av8Sgo+fkbEbQWOw/kwh89AZ5fNA8TYfcooG6TG1ZOL2WcPUrngIMIK8dBJitQ8QEU0zbncQ0CX4CQ==", + "dev": true, + "dependencies": { + "deeks": "3.1.0", + "doc-path": "4.1.1" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, + "node_modules/llm-chunk": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/llm-chunk/-/llm-chunk-0.0.1.tgz", + "integrity": "sha512-n9fHgsSiJb7vXZiC5c4XV6rme+tC7WX/cWH6EJvPPmMOMwOZ9xdg/U9LY5Qhmixd3K1PdRB0FVOdzoJF2HUZbg==" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dev": true, + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/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==", + "dev": true + }, + "node_modules/long": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-ensure": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz", + "integrity": "sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw==" + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openapi3-ts": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.4.0.tgz", + "integrity": "sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==", + "dev": true, + "dependencies": { + "yaml": "^2.5.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pdf-parse": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-1.1.1.tgz", + "integrity": "sha512-v6ZJ/efsBpGrGGknjtq9J/oC8tZWq0KWL5vQrk2GlzLEQPUDB1ex+13Rmidl1neNN358Jn9EHZw5y07FFtaC7A==", + "dependencies": { + "debug": "^3.1.0", + "node-ensure": "^0.0.0" + }, + "engines": { + "node": ">=6.8.1" + } + }, + "node_modules/pdf-parse/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/pdf-parse/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==" + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "dev": true, + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.0.tgz", + "integrity": "sha512-/Tvpny/RVVicqlYTKwt/GtpZRsPG1CmJNhxVKGz+Sy/4MONfXCVNK69MFgGKdUt0/324q3ClI2dICcPgISrC8g==", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/require-in-the-middle/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/require-in-the-middle/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==" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "dev": true, + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/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==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "dev": true, + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "dev": true + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/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==", + "dev": true + }, + "node_modules/teeny-request/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/terminate": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/terminate/-/terminate-2.8.0.tgz", + "integrity": "sha512-bcbjJEg0wY5nuJXvGxxHfmoEPkyHLCctUKO6suwtxy7jVSgGcgPeGwpbLDLELFhIaxCGRr3dPvyNg1yuz2V0eg==", + "dev": true, + "dependencies": { + "ps-tree": "^1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true, + "engines": { + "node": ">= 14.0.0" + } + }, + "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==" + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "dev": true, + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dev": true, + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", + "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} diff --git a/samples/js-schoolAgent/package.json b/samples/js-schoolAgent/package.json index 3ebc1299b..2ab17b892 100644 --- a/samples/js-schoolAgent/package.json +++ b/samples/js-schoolAgent/package.json @@ -8,7 +8,7 @@ "dev": "tsx --no-warnings --watch src/terminal.ts", "genkit:dev": "genkit start -- npm run dev", "compile": "tsc", - "build": "pnpm build:clean && npm run compile", + "build": "npm build:clean && npm run compile", "build:clean": "rimraf ./lib", "build:watch": "tsc --watch" }, diff --git a/samples/prompts/package-lock.json b/samples/prompts/package-lock.json new file mode 100644 index 000000000..49e253a71 --- /dev/null +++ b/samples/prompts/package-lock.json @@ -0,0 +1,5461 @@ +{ + "name": "prompts", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "prompts", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@genkit-ai/googleai": "^0.9.0-rc || ^0.9", + "express": "^4.21.0", + "genkit": "^0.9.0-rc || ^0.9", + "zod": "^3.23.8" + }, + "devDependencies": { + "genkit-cli": "^0.9.0-rc || ^0.9", + "typescript": "^5.5.4" + } + }, + "node_modules/@asteasolutions/zod-to-openapi": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.0.tgz", + "integrity": "sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==", + "dev": true, + "dependencies": { + "openapi3-ts": "^4.1.2" + }, + "peerDependencies": { + "zod": "^3.20.2" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@genkit-ai/ai": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-0.9.12.tgz", + "integrity": "sha512-xyVVAIGKNpj5zCkoEfWZkzwctl0/hmpX6vKZgdgMH2MiqP5LzTp7rUekBMon8c1rMDVAze97QVSjAmZIoMLSlA==", + "dependencies": { + "@genkit-ai/core": "0.9.12", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.11.19", + "colorette": "^2.0.20", + "json5": "^2.2.3", + "node-fetch": "^3.3.2", + "partial-json": "^0.1.7", + "uuid": "^10.0.0" + } + }, + "node_modules/@genkit-ai/core": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-0.9.12.tgz", + "integrity": "sha512-QPJZ3TL5Iq2fyeo30MpUjd3ZLcYQf97RsitDZhMbGy3vMwbgig0nhEbJ6v/qaWsOMqSfIxJE/gETY3mMts1vRg==", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "async-mutex": "^0.5.0", + "body-parser": "^1.20.3", + "cors": "^2.8.5", + "express": "^4.21.0", + "get-port": "^5.1.0", + "json-schema": "^0.4.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/dotprompt": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/dotprompt/-/dotprompt-0.9.12.tgz", + "integrity": "sha512-eEHBRzRVemiPuqCBbXiLgltNWpmCHmC+gVHBhsAnrfOYBlwmPvh2nnAPBXGYnkDH87PuN11jg8YJQmO4kQuoSw==", + "dependencies": { + "@genkit-ai/ai": "0.9.12", + "@genkit-ai/core": "0.9.12", + "front-matter": "^4.0.2", + "handlebars": "^4.7.8", + "node-fetch": "^3.3.2" + } + }, + "node_modules/@genkit-ai/googleai": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-0.9.12.tgz", + "integrity": "sha512-q6bX9Nq4xVpzH4vd9W+rg4xO7SVodG516K4BVM04DtxU5w0ogwLl/mRPEU0VIoISjD6FwaC4sPjPkEerTp8a4g==", + "dependencies": { + "@google/generative-ai": "^0.21.0", + "google-auth-library": "^9.6.3", + "node-fetch": "^3.3.2" + }, + "peerDependencies": { + "genkit": "0.9.12" + } + }, + "node_modules/@genkit-ai/telemetry-server": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-0.9.12.tgz", + "integrity": "sha512-8w1O9LOUtMCGc8bx+Vcd5D68L8ctM6H544Vxnfvy8gx4JHV5dj7vQiI9YDgm65pgP15pytQzt1TcRtOtYoimFw==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@genkit-ai/tools-common": "0.9.12", + "@google-cloud/firestore": "^7.6.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "async-mutex": "^0.5.0", + "express": "^4.21.0", + "zod": "^3.22.4" + } + }, + "node_modules/@genkit-ai/tools-common": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-0.9.12.tgz", + "integrity": "sha512-A9ToM/CY6vxcBSw4o47Q7qrpMaRsk4P6ZuzoDMlCzCVI+RZN5ulTL4LXMrq6nQ/KxY1GT94bSoulROXBMzW/Gw==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@trpc/server": "10.45.0", + "adm-zip": "^0.5.12", + "axios": "^1.7.7", + "body-parser": "^1.20.2", + "chokidar": "^3.5.3", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "configstore": "^5.0.1", + "express": "^4.21.0", + "get-port": "5.1.1", + "glob": "^10.3.12", + "inquirer": "^8.2.0", + "js-yaml": "^4.1.0", + "json-2-csv": "^5.5.1", + "json-schema": "^0.4.0", + "terminate": "^2.6.1", + "tsx": "^4.19.2", + "uuid": "^9.0.1", + "winston": "^3.11.0", + "yaml": "^2.4.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/tools-common/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==", + "dev": true + }, + "node_modules/@genkit-ai/tools-common/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@genkit-ai/tools-common/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@google-cloud/firestore": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.0.tgz", + "integrity": "sha512-88uZ+jLsp1aVMj7gh3EKYH1aulTAMFAp8sH/v5a9w8q8iqSG27RiWLoxSAFr/XocZ9hGiWH1kEnBw+zl3xAgNA==", + "dev": true, + "dependencies": { + "@opentelemetry/api": "^1.3.0", + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.3.3", + "protobufjs": "^7.2.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google/generative-ai": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz", + "integrity": "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz", + "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz", + "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz", + "integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", + "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz", + "integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz", + "integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz", + "integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", + "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", + "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz", + "integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", + "@opentelemetry/exporter-zipkin": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/sdk-trace-node": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", + "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.25.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/propagator-b3": "1.25.1", + "@opentelemetry/propagator-jaeger": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trpc/server": { + "version": "10.45.0", + "resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.45.0.tgz", + "integrity": "sha512-2Fwzv6nqpE0Ie/G7PeS0EVR89zLm+c1Mw7T+RAGtU807j4oaUx0zGkBXTu5u9AI+j+BYNN2GZxJcuDTAecbr1A==", + "dev": true, + "funding": [ + "https://trpc.io/sponsor" + ] + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "dev": true + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.17.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", + "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "dev": true, + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "dev": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "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==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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==", + "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==" + }, + "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==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deeks": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/deeks/-/deeks-3.1.0.tgz", + "integrity": "sha512-e7oWH1LzIdv/prMQ7pmlDlaVoL64glqzvNgkgQNgyec9ORPHrT2jaOqMtRyqJuwWjtfb6v+2rk9pmaHj+F137A==", + "dev": true, + "engines": { + "node": ">= 16" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/doc-path": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-4.1.1.tgz", + "integrity": "sha512-h1ErTglQAVv2gCnOpD3sFS6uolDbOKHDU1BZq+Kl3npPqroU3dYL42lUgMfd5UimlwtRgp7C9dLGwqQ5D2HYgQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "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==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/extract-zip/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==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, + "node_modules/front-matter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", + "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", + "dependencies": { + "js-yaml": "^3.13.1" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "dependencies": { + "gaxios": "^6.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/genkit": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-0.9.12.tgz", + "integrity": "sha512-m1VQE/yhuii0y1aGTnkoSesSXTNE25q1s7vv5YVgJWa/t2gOXuznZOoHTJ847f/3mKC7fgnV7xGI+t/+7wbe0g==", + "dependencies": { + "@genkit-ai/ai": "0.9.12", + "@genkit-ai/core": "0.9.12", + "@genkit-ai/dotprompt": "0.9.12", + "uuid": "^10.0.0" + } + }, + "node_modules/genkit-cli": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-0.9.12.tgz", + "integrity": "sha512-Qsk7TmVBxghTT+0cRe2j0fYJboUIfWhuUoeVbWshranye6tConGMyDEZdXBIeMWIixSbG7k8a7a2INdRdjcQJw==", + "dev": true, + "dependencies": { + "@genkit-ai/telemetry-server": "0.9.12", + "@genkit-ai/tools-common": "0.9.12", + "axios": "^1.7.7", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "extract-zip": "^2.0.1", + "get-port": "5.1.1", + "inquirer": "^8.2.0", + "open": "^6.3.0", + "ora": "^5.4.1" + }, + "bin": { + "genkit": "dist/bin/genkit.js" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", + "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", + "dev": true, + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/google-gax/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/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==", + "dev": true + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/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==" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/import-in-the-middle": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.12.0.tgz", + "integrity": "sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-2-csv": { + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.8.tgz", + "integrity": "sha512-eMQHOwV+av8Sgo+fkbEbQWOw/kwh89AZ5fNA8TYfcooG6TG1ZOL2WcPUrngIMIK8dBJitQ8QEU0zbncQ0CX4CQ==", + "dev": true, + "dependencies": { + "deeks": "3.1.0", + "doc-path": "4.1.1" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dev": true, + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/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==", + "dev": true + }, + "node_modules/long": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openapi3-ts": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.4.0.tgz", + "integrity": "sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==", + "dev": true, + "dependencies": { + "yaml": "^2.5.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "dev": true, + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.0.tgz", + "integrity": "sha512-/Tvpny/RVVicqlYTKwt/GtpZRsPG1CmJNhxVKGz+Sy/4MONfXCVNK69MFgGKdUt0/324q3ClI2dICcPgISrC8g==", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/require-in-the-middle/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/require-in-the-middle/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==" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "dev": true, + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/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==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "dev": true, + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "dev": true + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/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==", + "dev": true + }, + "node_modules/teeny-request/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/terminate": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/terminate/-/terminate-2.8.0.tgz", + "integrity": "sha512-bcbjJEg0wY5nuJXvGxxHfmoEPkyHLCctUKO6suwtxy7jVSgGcgPeGwpbLDLELFhIaxCGRr3dPvyNg1yuz2V0eg==", + "dev": true, + "dependencies": { + "ps-tree": "^1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true, + "engines": { + "node": ">= 14.0.0" + } + }, + "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==" + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "dev": true, + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dev": true, + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", + "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} From a675bb30ba1c2d6f74b8e48f1631125ca084de85 Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Thu, 30 Jan 2025 16:15:12 -0500 Subject: [PATCH 415/562] fix: rag doc updates (#1677) --------- Co-authored-by: Pavel Jbanov --- docs/rag.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/rag.md b/docs/rag.md index 4176da66f..80cb878aa 100644 --- a/docs/rag.md +++ b/docs/rag.md @@ -226,12 +226,12 @@ export const indexMenu = ai.defineFlow( filePath = path.resolve(filePath); // Read the pdf. - const pdfTxt = await run('extract-text', () => + const pdfTxt = await ai.run('extract-text', () => extractTextFromPdf(filePath) ); // Divide the pdf text into segments. - const chunks = await run('chunk-it', async () => + const chunks = await ai.run('chunk-it', async () => chunk(pdfTxt, chunkingConfig) ); @@ -266,12 +266,13 @@ which you should not use in production. ```ts import { devLocalRetrieverRef } from '@genkit-ai/dev-local-vectorstore'; +import { gemini } from '@genkit-ai/vertexai'; // Define the retriever reference export const menuRetriever = devLocalRetrieverRef('menuQA'); export const menuQAFlow = ai.defineFlow( - { name: 'menuQA', inputSchema: z.string(), outputSchema: z.string() }, + { name: "menuQA", inputSchema: z.string(), outputSchema: z.string() }, async (input: string) => { // retrieve relevant documents const docs = await ai.retrieve({ @@ -282,6 +283,7 @@ export const menuQAFlow = ai.defineFlow( // generate a response const { text } = await ai.generate({ + model: gemini('gemini-1.5-flash'), prompt: ` You are acting as a helpful AI assistant that can answer questions about the food available on the menu at Genkit Grub Pub. @@ -299,6 +301,15 @@ Question: ${input}`, ); ``` +#### Run the retriever flow + +```posix-terminal +genkit flow:run menuQA '"Recommend a dessert from the menu while avoiding dairy and nuts"' +``` + +The output for this command should contain a response from the model, grounded +in the indexed `menu.pdf` file. + ## Write your own indexers and retrievers It's also possible to create your own retriever. This is useful if your From 4db4e9a7d47e9c227a6606093da91e9624129ac1 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 30 Jan 2025 17:46:54 -0500 Subject: [PATCH 416/562] feat(js/ai): improving definePrompt backwards compatibility, to ease the transition (#1722) --- js/genkit/src/genkit.ts | 41 +++++++++++++++++++++++- js/genkit/tests/prompts_test.ts | 57 +++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index e780a04ea..7d7f87648 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -104,6 +104,7 @@ import { ActionContext, FlowConfig, FlowFn, + GenkitError, JSONSchema, ReflectionServer, StreamingCallback, @@ -121,6 +122,14 @@ import { logger } from './logging.js'; import { GenkitPlugin } from './plugin.js'; import { Registry } from './registry.js'; +/** + * @deprecated use `ai.definePrompt({messages: fn})` + */ +export type PromptFn< + I extends z.ZodTypeAny = z.ZodTypeAny, + CustomOptionsSchema extends z.ZodTypeAny = z.ZodTypeAny, +> = (input: z.infer) => Promise>; + /** * Options for initializing Genkit. */ @@ -343,6 +352,7 @@ export class Genkit implements HasRegistry { return executablePrompt; } + /** * Defines and registers a prompt based on a function. * @@ -382,8 +392,37 @@ export class Genkit implements HasRegistry { O extends z.ZodTypeAny = z.ZodTypeAny, CustomOptions extends z.ZodTypeAny = z.ZodTypeAny, >( - options: PromptConfig + options: PromptConfig, + /** @deprecated use `options.messages` with a template string instead. */ + templateOrFn?: string | PromptFn ): ExecutablePrompt, O, CustomOptions> { + // For backwards compatibility... + if (templateOrFn) { + if (options.messages) { + throw new GenkitError({ + status: 'INVALID_ARGUMENT', + message: + 'Cannot specify template/function argument and `options.messages` at the same time', + }); + } + if (typeof templateOrFn === 'string') { + return definePrompt(this.registry, { + ...options, + messages: templateOrFn, + }); + } else { + // it's the PromptFn + return definePrompt(this.registry, { + ...options, + messages: async (input) => { + const response = await ( + templateOrFn as PromptFn, CustomOptions> + )(input); + return response.messages; + }, + }); + } + } return definePrompt(this.registry, options); } diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index 5075d8700..ea236062f 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -663,6 +663,63 @@ describe('definePrompt', () => { assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); }); + it('calls legacy prompt with default model', async () => { + const hi = ai.definePrompt( + { + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), + }, + }, + async (input) => { + return { + messages: [ + { role: 'user', content: [{ text: `hi ${input.name}` }] }, + ], + }; + } + ); + + const response = await hi({ name: 'Genkit' }); + assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); + }); + + it('calls legacy prompt with default model', async () => { + const hi = ai.definePrompt( + { + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), + }, + }, + 'hi {{ name }}' + ); + + const response = await hi({ name: 'Genkit' }); + assert.strictEqual(response.text, 'Echo: hi Genkit; config: {}'); + }); + + it('thrown on prompt with both legacy template and messages', async () => { + assert.throws(() => + ai.definePrompt( + { + name: 'hi', + input: { + schema: z.object({ + name: z.string(), + }), + }, + messages: 'something', + }, + 'hi {{ name }}' + ) + ); + }); + it('calls prompt with default model with config', async () => { const hi = ai.definePrompt({ name: 'hi', From 482d5c7cc28bdb461a9112e650e03c9c614f3d02 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Thu, 30 Jan 2025 19:19:43 -0500 Subject: [PATCH 417/562] fix(js/ai): default to JSON format when schema is set (#1728) --- js/ai/src/generate.ts | 5 ++- js/genkit/tests/prompts/output.prompt | 11 ++++++ js/genkit/tests/prompts_test.ts | 54 +++++++++++++++++++++++++-- 3 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 js/genkit/tests/prompts/output.prompt diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index 89b4a7abd..feee4433c 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -358,7 +358,10 @@ export async function generate< }); // If is schema is set but format is not explicitly set, default to `json` format. - if (resolvedOptions.output?.schema && !resolvedOptions.output?.format) { + if ( + (resolvedOptions.output?.schema || resolvedOptions.output?.jsonSchema) && + !resolvedOptions.output?.format + ) { resolvedOptions.output.format = 'json'; } const resolvedFormat = await resolveFormat(registry, resolvedOptions.output); diff --git a/js/genkit/tests/prompts/output.prompt b/js/genkit/tests/prompts/output.prompt new file mode 100644 index 000000000..ac0987a69 --- /dev/null +++ b/js/genkit/tests/prompts/output.prompt @@ -0,0 +1,11 @@ +--- +model: staticResponseModel +input: + schema: + name: string +output: + schema: + bar: string +--- + +Hi {{ name }} \ No newline at end of file diff --git a/js/genkit/tests/prompts_test.ts b/js/genkit/tests/prompts_test.ts index ea236062f..14d547c81 100644 --- a/js/genkit/tests/prompts_test.ts +++ b/js/genkit/tests/prompts_test.ts @@ -17,6 +17,7 @@ import { ModelMiddleware, modelRef } from '@genkit-ai/ai/model'; import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; +import { stripUndefinedProps } from '../../core/src'; import { GenkitBeta, genkit } from '../src/beta'; import { PromptAction, z } from '../src/index'; import { @@ -61,7 +62,7 @@ const wrapResponse: ModelMiddleware = async (req, next) => { }; }; -describe('definePrompt - functional', () => { +describe('definePrompt', () => { let ai: GenkitBeta; beforeEach(() => { @@ -172,7 +173,7 @@ describe('definePrompt - functional', () => { }); }); -describe('definePrompt - dotprompt', () => { +describe.only('definePrompt', () => { describe('default model', () => { let ai: GenkitBeta; @@ -309,7 +310,7 @@ describe('definePrompt - dotprompt', () => { }); }); - describe('default model ref', () => { + describe.only('default model ref', () => { let ai: GenkitBeta; beforeEach(() => { @@ -317,6 +318,7 @@ describe('definePrompt - dotprompt', () => { model: modelRef({ name: 'echoModel', }), + promptDir: './tests/prompts', }); defineEchoModel(ai); }); @@ -400,6 +402,52 @@ describe('definePrompt - dotprompt', () => { assert.deepStrictEqual(foo, { bar: 'baz' }); }); + it('defaults to json format from a loaded prompt', async () => { + defineStaticResponseModel(ai, { + role: 'model', + content: [ + { + text: '```json\n{bar: "baz"}\n```', + }, + ], + }); + const hi = ai.prompt('output'); + + const response = await hi({ name: 'Genkit' }); + const foo = response.output; + assert.deepStrictEqual(stripUndefinedProps(response.request), { + config: {}, + messages: [ + { + content: [ + { + text: 'Hi Genkit', + }, + ], + role: 'user', + }, + ], + output: { + constrained: true, + contentType: 'application/json', + format: 'json', + schema: { + additionalProperties: false, + properties: { + bar: { + type: 'string', + }, + }, + required: ['bar'], + type: 'object', + }, + }, + tools: [], + }); + + assert.deepStrictEqual(foo, { bar: 'baz' }); + }); + it('streams dotprompt with default model', async () => { const hi = ai.definePrompt({ name: 'hi', From c5b18407835076b401801d95eccb7aa38cc68bcd Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:26:12 +0000 Subject: [PATCH 418/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.12 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 888511a5b..161743581 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 5073cb6d7c4912d385e2bf93b6efc546234350de Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:26:14 +0000 Subject: [PATCH 419/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.12 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 8bfef5fc5..434903fa8 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From 4f5c78b5feb74695a64bdbd625385378d095cfca Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:26:16 +0000 Subject: [PATCH 420/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.12 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index 6d6593200..c95fd1203 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From 0699dc65a73789f2c1cfd3416f495e1dbf4622fd Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:05 +0000 Subject: [PATCH 421/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.12 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 066093f23..0bd677a81 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From 605c722834deb0c2e5e580b6553d99bb408bec6b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:07 +0000 Subject: [PATCH 422/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.12 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index 43397fae3..1d6e7239a 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From b5bedbe19cc6ec064cbc441df35756eaac8e887a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:10 +0000 Subject: [PATCH 423/562] chore: bump genkit version to genkit@1.0.0-rc.12 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index ed5bd7934..228679de4 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From 16f4d673ed8fe444e185d1db53c96051284a7f52 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:13 +0000 Subject: [PATCH 424/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.12 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index 9ab2a3c64..22bc91232 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From 48471a246e3f374872be81db92a6352ecf913cf9 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:15 +0000 Subject: [PATCH 425/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.12 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 90fa14b88..273ffb844 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From 3709c86c935a3c12af0532f29f1e763b598bd27a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:18 +0000 Subject: [PATCH 426/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.12 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 2815c7e69..d2c2a5738 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From 0df1082f765f860b73423bb3e1372f3433ac5f3c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:21 +0000 Subject: [PATCH 427/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.12 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 7191bed78..0f91d0487 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From dfe8b70e39afe798b71f6261f969be0449f52b98 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:23 +0000 Subject: [PATCH 428/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.12 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index 7e673e4c1..ec97e5fc1 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From 28036708e4db9a1086659fd479fae78e8d11b56b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:26 +0000 Subject: [PATCH 429/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.12 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 822274f84..07992708c 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From e5642fac6e8ee5880309203fb0a2d3c5d6121aa1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:29 +0000 Subject: [PATCH 430/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.12 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index ad79f6d93..523e51ba5 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From 32854940baf786666b016d8f4d3ba016ac2a019c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:32 +0000 Subject: [PATCH 431/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.12 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index 1c4e60396..0dc4c21ca 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From 23338731c03e9494724851b6b36f4c81ec246cd6 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:34 +0000 Subject: [PATCH 432/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.12 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index 1c4524a60..08d10f745 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From 15d964d8725ad43743e77c42c8314c93960e0418 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:37 +0000 Subject: [PATCH 433/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.12 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 2d57475bf..60708042c 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From 70c9e03e846ab134ecba301df6493e4676b5cbdb Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:40 +0000 Subject: [PATCH 434/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.12 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index e5fb5d6b1..64c5fe599 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From d312acd378d4e2e3e635ac51b09badb2c51a9a16 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:42 +0000 Subject: [PATCH 435/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.12 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index f6cbcdcf0..75e40af48 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From d5eade3446ae543c9549aaddb113cea1de689b3c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 00:27:46 +0000 Subject: [PATCH 436/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.12 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index d4984ebf1..2bbfb4bec 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.11", + "version": "1.0.0-rc.12", "type": "commonjs", "scripts": { "check": "tsc", From 06bd416053a8e5b4f1cc83c90c394693627ca1e7 Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Fri, 31 Jan 2025 10:02:43 -0500 Subject: [PATCH 437/562] chore: Update js-coffeeshop to 1.0 (#1734) --- samples/js-coffee-shop/README.md | 20 +- samples/js-coffee-shop/package-lock.json | 472 +++++++++-------------- samples/js-coffee-shop/package.json | 20 +- 3 files changed, 219 insertions(+), 293 deletions(-) diff --git a/samples/js-coffee-shop/README.md b/samples/js-coffee-shop/README.md index 618a80f5e..79958cd6a 100644 --- a/samples/js-coffee-shop/README.md +++ b/samples/js-coffee-shop/README.md @@ -1,6 +1,24 @@ ## Running the sample ```bash +export GOOGLE_GENAI_API_KEY= npm i -genkit start +npm run genkit:dev ``` + +or try it out on Project IDX, our Cloud-based IDE + +
+ + + + Open in IDX + + diff --git a/samples/js-coffee-shop/package-lock.json b/samples/js-coffee-shop/package-lock.json index 527c1557b..9239152f5 100644 --- a/samples/js-coffee-shop/package-lock.json +++ b/samples/js-coffee-shop/package-lock.json @@ -9,19 +9,19 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@genkit-ai/dev-local-vectorstore": "^0.9.0-rc || ^0.9", - "@genkit-ai/evaluator": "^0.9.0-rc || ^0.9", - "@genkit-ai/firebase": "^0.9.0-rc || ^0.9", - "@genkit-ai/googleai": "^0.9.0-rc || ^0.9", - "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9", - "genkit": "^0.9.0-rc || ^0.9", - "genkitx-chromadb": "^0.9.0-rc || ^0.9", - "genkitx-ollama": "^0.9.0-rc || ^0.9", - "genkitx-pinecone": "^0.9.0-rc || ^0.9", + "@genkit-ai/dev-local-vectorstore": "^1.0.0-rc.12", + "@genkit-ai/evaluator": "^1.0.0-rc.12", + "@genkit-ai/firebase": "^1.0.0-rc.12", + "@genkit-ai/googleai": "^1.0.0-rc.12", + "@genkit-ai/vertexai": "^1.0.0-rc.12", + "genkit": "^1.0.0-rc.12", + "genkitx-chromadb": "^1.0.0-rc.12", + "genkitx-ollama": "^1.0.0-rc.12", + "genkitx-pinecone": "^1.0.0-rc.12", "zod": "^3.22.4" }, "devDependencies": { - "genkit-cli": "^0.9.0-rc || ^0.9", + "genkit-cli": "^1.0.0-rc.12", "rimraf": "^6.0.1", "typescript": "^5.3.3" } @@ -41,14 +41,6 @@ "web-streams-polyfill": "^3.2.1" } }, - "node_modules/@anthropic-ai/sdk/node_modules/@types/node": { - "version": "18.19.74", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", - "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, "node_modules/@anthropic-ai/sdk/node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -68,11 +60,6 @@ } } }, - "node_modules/@anthropic-ai/sdk/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, "node_modules/@anthropic-ai/vertex-sdk": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@anthropic-ai/vertex-sdk/-/vertex-sdk-0.4.3.tgz", @@ -593,24 +580,50 @@ } }, "node_modules/@genkit-ai/ai": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-0.9.12.tgz", - "integrity": "sha512-xyVVAIGKNpj5zCkoEfWZkzwctl0/hmpX6vKZgdgMH2MiqP5LzTp7rUekBMon8c1rMDVAze97QVSjAmZIoMLSlA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.12.tgz", + "integrity": "sha512-1hofDfuTEVDfryy7klgeJwCyXOp4wEZ+VPU34Dts9S5PYoN1nb23KAhnz+6dHgM42REM1PI5Zh2Wq8O10BXIww==", "dependencies": { - "@genkit-ai/core": "0.9.12", + "@genkit-ai/core": "1.0.0-rc.12", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.11.19", "colorette": "^2.0.20", + "dotprompt": "^1.0.0-dev.3 || ^1", "json5": "^2.2.3", "node-fetch": "^3.3.2", "partial-json": "^0.1.7", "uuid": "^10.0.0" } }, + "node_modules/@genkit-ai/ai/node_modules/@types/node": { + "version": "20.17.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", + "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@genkit-ai/ai/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/@genkit-ai/ai/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@genkit-ai/core": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-0.9.12.tgz", - "integrity": "sha512-QPJZ3TL5Iq2fyeo30MpUjd3ZLcYQf97RsitDZhMbGy3vMwbgig0nhEbJ6v/qaWsOMqSfIxJE/gETY3mMts1vRg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.12.tgz", + "integrity": "sha512-3K7GVXR1vnJu2ANICbojxKpBoxIncuGIvn0NJ7T+s3/IOBQcusmkUyJoc4TdeRsZdX693J+oMUSx+ZE18s28eQ==", "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -618,11 +631,13 @@ "@opentelemetry/sdk-metrics": "^1.25.0", "@opentelemetry/sdk-node": "^0.52.0", "@opentelemetry/sdk-trace-base": "^1.25.0", + "@types/json-schema": "^7.0.15", "ajv": "^8.12.0", "ajv-formats": "^3.0.1", "async-mutex": "^0.5.0", "body-parser": "^1.20.3", "cors": "^2.8.5", + "dotprompt": "^1.0.0-dev.3 || ^1", "express": "^4.21.0", "get-port": "^5.1.0", "json-schema": "^0.4.0", @@ -631,49 +646,51 @@ } }, "node_modules/@genkit-ai/dev-local-vectorstore": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/dev-local-vectorstore/-/dev-local-vectorstore-0.9.12.tgz", - "integrity": "sha512-wkrLRqTw1N50hZpQlC1Hq8yOWapyz3BiC03auK93j85sL2L6tIFQ1zFHw7qKD+t39fiTmpocl8O6spLEerUKoA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/dev-local-vectorstore/-/dev-local-vectorstore-1.0.0-rc.12.tgz", + "integrity": "sha512-IOKUsp1MPG7TXHIURY4Wl8x4IPBiqy0QEwnu8TV11nA251fd4M1woflfOfM4BGZeX10IFe3bOPIo1j0SbGAZJA==", "dependencies": { "compute-cosine-similarity": "^1.1.0", "ts-md5": "^1.3.1" }, "peerDependencies": { - "genkit": "0.9.12" - } - }, - "node_modules/@genkit-ai/dotprompt": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/dotprompt/-/dotprompt-0.9.12.tgz", - "integrity": "sha512-eEHBRzRVemiPuqCBbXiLgltNWpmCHmC+gVHBhsAnrfOYBlwmPvh2nnAPBXGYnkDH87PuN11jg8YJQmO4kQuoSw==", - "dependencies": { - "@genkit-ai/ai": "0.9.12", - "@genkit-ai/core": "0.9.12", - "front-matter": "^4.0.2", - "handlebars": "^4.7.8", - "node-fetch": "^3.3.2" + "genkit": "^1.0.0-rc.12" } }, "node_modules/@genkit-ai/evaluator": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/evaluator/-/evaluator-0.9.12.tgz", - "integrity": "sha512-JbZfwwqDf6IP84cGt+QZlSB0h+FcdAM7Ur6A/lL5GJdMudIjRdxmhu3FEu/kWKK59EQlJShGBW5/Vyw93ynPqw==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/evaluator/-/evaluator-1.0.0-rc.12.tgz", + "integrity": "sha512-E1gVBIHYPSUv0+56DEcsFZeA2XgU3ZfSeAXqrTrtrSmd5Hi9Iav1NLj+28LaapIbFdZnTlKMigJovX3TXbXg1Q==", "dependencies": { - "@genkit-ai/dotprompt": "0.9.12", "compute-cosine-similarity": "^1.1.0", + "dotprompt": "^1.0.0-dev.3 || ^1", "node-fetch": "^3.3.2", "path": "^0.12.7" }, "peerDependencies": { - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.12" + } + }, + "node_modules/@genkit-ai/express": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/express/-/express-1.0.0-rc.12.tgz", + "integrity": "sha512-AiW1IGkE/7g4LPIpNj0OjysJygXNpVCKXitQjrAd/Q7qUQhGPZmzv2LyVTgEkhuHWZJ4VzTRLnWgBLewaXiqpA==", + "dependencies": { + "body-parser": "^1.20.3", + "cors": "^2.8.5" + }, + "peerDependencies": { + "express": "^4.21.1", + "genkit": "^1.0.0-rc.12" } }, "node_modules/@genkit-ai/firebase": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/firebase/-/firebase-0.9.12.tgz", - "integrity": "sha512-PbEAPO3GTENaJR5RUqrq88YIVxgkVi2pc82kOCrkJDpw/+PcyRIZcgvbQYSAzB932yVU0sy33zzdUfg9t2thMA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/firebase/-/firebase-1.0.0-rc.12.tgz", + "integrity": "sha512-PwlMwzqY+VlCUyJAGUk5Zab5+1CFf17z7ohIa2rLGNJs0FAgvRAMp9gZpp0/LpGxZFa9iZ82Vbv/MK290VwL0A==", "dependencies": { - "@genkit-ai/google-cloud": "0.9.12", + "@genkit-ai/express": "^1.0.0-rc.12", + "@genkit-ai/google-cloud": "^1.0.0-rc.12", "express": "^4.21.0", "google-auth-library": "^9.6.3" }, @@ -681,13 +698,13 @@ "@google-cloud/firestore": "^7.6.0", "firebase-admin": ">=12.2", "firebase-functions": ">=4.8", - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.12" } }, "node_modules/@genkit-ai/google-cloud": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/google-cloud/-/google-cloud-0.9.12.tgz", - "integrity": "sha512-hYDvdQmGdI/JC6hUa6hMs6+Kc04wmi+/54zJIB+EOiNLd6fJ193B+q/pHqI4UxvofJPsf9C6VTK5Q54+yyQm7w==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/google-cloud/-/google-cloud-1.0.0-rc.12.tgz", + "integrity": "sha512-/trGdt4PfKi9om3KpzN5keYM0iThUAIkKbhxv2pP6ZAhBYnZD3zMaKde7bgjP7cqcIkcH2QY0zHr6ad1M0+MZg==", "dependencies": { "@google-cloud/logging-winston": "^6.0.0", "@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.19.0", @@ -708,30 +725,30 @@ "winston": "^3.12.0" }, "peerDependencies": { - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.12" } }, "node_modules/@genkit-ai/googleai": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-0.9.12.tgz", - "integrity": "sha512-q6bX9Nq4xVpzH4vd9W+rg4xO7SVodG516K4BVM04DtxU5w0ogwLl/mRPEU0VIoISjD6FwaC4sPjPkEerTp8a4g==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-1.0.0-rc.12.tgz", + "integrity": "sha512-P56jclocNjgwSBizjieMc99D7WJYXAAaI7WOjkuij7oyt6BOvzCZ9rcuTJBtH/n3PRCD1P0ixt8l3m6SrxYjNQ==", "dependencies": { "@google/generative-ai": "^0.21.0", "google-auth-library": "^9.6.3", "node-fetch": "^3.3.2" }, "peerDependencies": { - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.12" } }, "node_modules/@genkit-ai/telemetry-server": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-0.9.12.tgz", - "integrity": "sha512-8w1O9LOUtMCGc8bx+Vcd5D68L8ctM6H544Vxnfvy8gx4JHV5dj7vQiI9YDgm65pgP15pytQzt1TcRtOtYoimFw==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.12.tgz", + "integrity": "sha512-hP1qQrFkarq3+A4KQITjoGaw52MBOGKIkRiqr9HbYYXd8KOQiJ7KNo9Hv7VixphG/WQ700RUMvqjB9H4DzXZyg==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", - "@genkit-ai/tools-common": "0.9.12", + "@genkit-ai/tools-common": "1.0.0-rc.12", "@google-cloud/firestore": "^7.6.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -745,14 +762,16 @@ } }, "node_modules/@genkit-ai/tools-common": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-0.9.12.tgz", - "integrity": "sha512-A9ToM/CY6vxcBSw4o47Q7qrpMaRsk4P6ZuzoDMlCzCVI+RZN5ulTL4LXMrq6nQ/KxY1GT94bSoulROXBMzW/Gw==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.12.tgz", + "integrity": "sha512-N6qvS92Xt2qqnsqoQfuKveQd+4bBeRhEWlwP0e99tfmcFSfL4MDj7JLvfaoNq2GGkr4xBURQjB9NGtojZksyNg==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", "@trpc/server": "10.45.0", "adm-zip": "^0.5.12", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", "axios": "^1.7.7", "body-parser": "^1.20.2", "chokidar": "^3.5.3", @@ -775,46 +794,15 @@ "zod-to-json-schema": "^3.22.4" } }, - "node_modules/@genkit-ai/tools-common/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==", - "dev": true - }, - "node_modules/@genkit-ai/tools-common/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@genkit-ai/tools-common/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@genkit-ai/vertexai": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/vertexai/-/vertexai-0.9.12.tgz", - "integrity": "sha512-lA6lwh5jgYgFr7RsFCXs+iASUZI2CmrGQEJCR+/qKJTNG0OyI4O7CdoT5SZSc1fK7oXeWH0/4x6mIwcngjy2Gw==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/vertexai/-/vertexai-1.0.0-rc.12.tgz", + "integrity": "sha512-PMosCY12/wmfuhIi3PfQaxenWOp/dMbs429/9T5llE4QxE0aGKSZJxr2hnab0+K+N7I+8bFWvvB/4B4LCOKG5A==", "dependencies": { "@anthropic-ai/sdk": "^0.24.3", "@anthropic-ai/vertex-sdk": "^0.4.0", "@google-cloud/aiplatform": "^3.23.0", - "@google-cloud/vertexai": "^1.9.0", + "@google-cloud/vertexai": "^1.9.2", "@mistralai/mistralai-gcp": "^1.3.5", "google-auth-library": "^9.14.2", "googleapis": "^140.0.1", @@ -826,7 +814,7 @@ "firebase-admin": ">=12.2" }, "peerDependencies": { - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.12" } }, "node_modules/@google-cloud/aiplatform": { @@ -863,19 +851,6 @@ "node": ">=14.0.0" } }, - "node_modules/@google-cloud/bigquery/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "optional": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@google-cloud/common": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-5.0.2.tgz", @@ -953,18 +928,6 @@ "winston": ">=3.2.1" } }, - "node_modules/@google-cloud/logging/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@google-cloud/opentelemetry-cloud-monitoring-exporter": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/@google-cloud/opentelemetry-cloud-monitoring-exporter/-/opentelemetry-cloud-monitoring-exporter-0.19.0.tgz", @@ -1347,9 +1310,9 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", - "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", "engines": { "node": ">=14" }, @@ -2875,17 +2838,6 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", - "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { "version": "1.25.1", "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", @@ -3132,6 +3084,11 @@ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, "node_modules/@types/jsonwebtoken": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.8.tgz", @@ -3173,11 +3130,11 @@ } }, "node_modules/@types/node": { - "version": "20.17.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", - "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", + "version": "18.19.74", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", + "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~5.26.4" } }, "node_modules/@types/node-fetch": { @@ -3446,12 +3403,10 @@ } }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/array-flatten": { "version": "1.1.1", @@ -3737,9 +3692,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==" + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==" }, "node_modules/cli-cursor": { "version": "3.1.0", @@ -4130,6 +4085,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/dotprompt": { + "version": "1.0.0-dev.3", + "resolved": "https://registry.npmjs.org/dotprompt/-/dotprompt-1.0.0-dev.3.tgz", + "integrity": "sha512-gAcIhG+vzZlrZcBN/lO8gEMUtXHKMLbs1+snJuZrPkZnNgnWrzz1hYN4vYotpng/5fHsYSPUEckECy6qUNSrsQ==", + "dependencies": { + "handlebars": "^4.7.8", + "yaml": "^2.5.0" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -4312,18 +4276,6 @@ "node": ">=0.8.0" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -4805,14 +4757,6 @@ "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", "dev": true }, - "node_modules/front-matter": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", - "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", - "dependencies": { - "js-yaml": "^3.13.1" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -4874,24 +4818,13 @@ } } }, - "node_modules/gaxios/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/gcp-metadata": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", - "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", "dependencies": { - "gaxios": "^6.0.0", + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" }, "engines": { @@ -4899,24 +4832,23 @@ } }, "node_modules/genkit": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/genkit/-/genkit-0.9.12.tgz", - "integrity": "sha512-m1VQE/yhuii0y1aGTnkoSesSXTNE25q1s7vv5YVgJWa/t2gOXuznZOoHTJ847f/3mKC7fgnV7xGI+t/+7wbe0g==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.12.tgz", + "integrity": "sha512-IsL8erWfFraJGENqYf+StJakoAkfsq4VHLhQkSU5iBNYways3BumsP1KT5CKNK3lpHGJWg65LrGznAxpJ5xTqQ==", "dependencies": { - "@genkit-ai/ai": "0.9.12", - "@genkit-ai/core": "0.9.12", - "@genkit-ai/dotprompt": "0.9.12", + "@genkit-ai/ai": "1.0.0-rc.12", + "@genkit-ai/core": "1.0.0-rc.12", "uuid": "^10.0.0" } }, "node_modules/genkit-cli": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-0.9.12.tgz", - "integrity": "sha512-Qsk7TmVBxghTT+0cRe2j0fYJboUIfWhuUoeVbWshranye6tConGMyDEZdXBIeMWIixSbG7k8a7a2INdRdjcQJw==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.12.tgz", + "integrity": "sha512-itEg/RDIs7CW9Oho7K0sb0luERwwksxkJvAkstINCroK8s9tkoDQAPlROKeV830sMeP9kze74EG2nySsOm6fvg==", "dev": true, "dependencies": { - "@genkit-ai/telemetry-server": "0.9.12", - "@genkit-ai/tools-common": "0.9.12", + "@genkit-ai/telemetry-server": "1.0.0-rc.12", + "@genkit-ai/tools-common": "1.0.0-rc.12", "axios": "^1.7.7", "colorette": "^2.0.20", "commander": "^11.1.0", @@ -4930,16 +4862,28 @@ "genkit": "dist/bin/genkit.js" } }, + "node_modules/genkit/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/genkitx-chromadb": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/genkitx-chromadb/-/genkitx-chromadb-0.9.12.tgz", - "integrity": "sha512-dBQY7vvT3EdGhl7EHFZdlhXabqSi2pbtgsndyiFQb634vjbDIdyNggvqh9w89p2pmqlf+OymMJyahRoSa+3AGw==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/genkitx-chromadb/-/genkitx-chromadb-1.0.0-rc.12.tgz", + "integrity": "sha512-V6E8gftM5oOQZr8Z8aKsRcWgVNqCN73KWdtajdA0c8eyGNVTL1FvZSoKE9Scmz1Oqy8F2SdL+4k2FWM7TFpEig==", "dependencies": { "chromadb": "1.8.1", "ts-md5": "^1.3.1" }, "peerDependencies": { - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.12" } }, "node_modules/genkitx-chromadb/node_modules/@google/generative-ai": { @@ -4981,23 +4925,26 @@ } }, "node_modules/genkitx-ollama": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/genkitx-ollama/-/genkitx-ollama-0.9.12.tgz", - "integrity": "sha512-mxj+Amvl6NRYxmAnYhO6yVXmG0aO+Fb5XL9OnUJnmauzqXkYyaxeblVgLKQtVJAYlnStZ50iYjMpUCMfd11QTA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/genkitx-ollama/-/genkitx-ollama-1.0.0-rc.12.tgz", + "integrity": "sha512-Y4qdl2YnmEIFNhcYV0pNDx/T8v0kQj3AoAq3WkO4xU/cCLuH3tEMN1uLnQpU6yjyESeTTfobCCMBEA/oMB3C1w==", + "dependencies": { + "ollama": "^0.5.9" + }, "peerDependencies": { - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.12" } }, "node_modules/genkitx-pinecone": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/genkitx-pinecone/-/genkitx-pinecone-0.9.12.tgz", - "integrity": "sha512-T4vgRMplRWeKGTsDfOZ1e/W1Y1VozMfYUod4UzOj4DfAn/QoS4YwR5TmybD+IRCBvb6cOaoXJ3qCMmhnw7S9vw==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/genkitx-pinecone/-/genkitx-pinecone-1.0.0-rc.12.tgz", + "integrity": "sha512-xyDgAoN08Y4cU6D9pXxsOE/bx042bKZir5rpKPVhlgEhUkhHs7i2paUV9R+N02ux7IqFKDkP2l9zyXiNijpKLQ==", "dependencies": { "@pinecone-database/pinecone": "^2.0.1", "ts-md5": "^1.3.1" }, "peerDependencies": { - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.12" } }, "node_modules/get-caller-file": { @@ -5170,16 +5117,12 @@ } } }, - "node_modules/google-gax/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "engines": { + "node": ">=14" } }, "node_modules/googleapis": { @@ -5210,18 +5153,6 @@ "node": ">=14.0.0" } }, - "node_modules/googleapis-common/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -5704,12 +5635,12 @@ } }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -6233,6 +6164,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ollama": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/ollama/-/ollama-0.5.12.tgz", + "integrity": "sha512-flVH1fn1c9NF7VV3bW9kSu0E+bYc40b4DxL/gS2Debhao35osJFRDiPOj9sIWTMvcyj78Paw1OuhfIe7uhDWfQ==", + "dependencies": { + "whatwg-fetch": "^3.6.20" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -6316,14 +6255,6 @@ } } }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.74", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", - "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, "node_modules/openai/node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -6343,11 +6274,6 @@ } } }, - "node_modules/openai/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, "node_modules/openapi3-ts": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.4.0.tgz", @@ -7211,11 +7137,6 @@ "node": "*" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -7426,18 +7347,6 @@ } } }, - "node_modules/teeny-request/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/terminate": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/terminate/-/terminate-2.8.0.tgz", @@ -7597,9 +7506,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/unique-string": { "version": "2.0.0", @@ -7653,9 +7562,9 @@ } }, "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -7882,7 +7791,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", - "dev": true, "bin": { "yaml": "bin.mjs" }, diff --git a/samples/js-coffee-shop/package.json b/samples/js-coffee-shop/package.json index b32e5b1b5..25cb395b6 100644 --- a/samples/js-coffee-shop/package.json +++ b/samples/js-coffee-shop/package.json @@ -16,19 +16,19 @@ "author": "", "license": "ISC", "dependencies": { - "genkit": "^0.9.0-rc || ^0.9", - "genkitx-chromadb": "^0.9.0-rc || ^0.9", - "@genkit-ai/dev-local-vectorstore": "^0.9.0-rc || ^0.9", - "@genkit-ai/firebase": "^0.9.0-rc || ^0.9", - "@genkit-ai/googleai": "^0.9.0-rc || ^0.9", - "genkitx-ollama": "^0.9.0-rc || ^0.9", - "genkitx-pinecone": "^0.9.0-rc || ^0.9", - "@genkit-ai/evaluator": "^0.9.0-rc || ^0.9", - "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9", + "genkit": "^1.0.0-rc.12", + "genkitx-chromadb": "^1.0.0-rc.12", + "@genkit-ai/dev-local-vectorstore": "^1.0.0-rc.12", + "@genkit-ai/firebase": "^1.0.0-rc.12", + "@genkit-ai/googleai": "^1.0.0-rc.12", + "genkitx-ollama": "^1.0.0-rc.12", + "genkitx-pinecone": "^1.0.0-rc.12", + "@genkit-ai/evaluator": "^1.0.0-rc.12", + "@genkit-ai/vertexai": "^1.0.0-rc.12", "zod": "^3.22.4" }, "devDependencies": { - "genkit-cli": "^0.9.0-rc || ^0.9", + "genkit-cli": "^1.0.0-rc.12", "rimraf": "^6.0.1", "typescript": "^5.3.3" } From d53b930de94c22fa06f787016c80398b2a1cb47e Mon Sep 17 00:00:00 2001 From: Michael Doyle Date: Fri, 31 Jan 2025 10:04:50 -0500 Subject: [PATCH 438/562] chore: add all vertex models to the dev-ui-gallery test app (#1715) --- js/testapps/dev-ui-gallery/src/genkit.ts | 37 ++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/js/testapps/dev-ui-gallery/src/genkit.ts b/js/testapps/dev-ui-gallery/src/genkit.ts index 56ae4ab92..718715e30 100644 --- a/js/testapps/dev-ui-gallery/src/genkit.ts +++ b/js/testapps/dev-ui-gallery/src/genkit.ts @@ -16,6 +16,7 @@ import { devLocalVectorstore } from '@genkit-ai/dev-local-vectorstore'; import { genkitEval, GenkitMetric } from '@genkit-ai/evaluator'; +import { enableFirebaseTelemetry } from '@genkit-ai/firebase'; import { gemini15Flash, googleAI } from '@genkit-ai/googleai'; import { textEmbedding004, vertexAI } from '@genkit-ai/vertexai'; import { @@ -24,6 +25,15 @@ import { } from '@genkit-ai/vertexai/evaluation'; import { claude35Sonnet, + claude35SonnetV2, + claude3Haiku, + claude3Opus, + claude3Sonnet, + codestral, + llama31, + llama32, + mistralLarge, + mistralNemo, vertexAIModelGarden, } from '@genkit-ai/vertexai/modelgarden'; import { genkit } from 'genkit'; @@ -34,6 +44,17 @@ import { pinecone } from 'genkitx-pinecone'; logger.setLogLevel('info'); +enableFirebaseTelemetry({ + forceDevExport: false, + metricExportIntervalMillis: 5000, + autoInstrumentation: true, + autoInstrumentationConfig: { + '@opentelemetry/instrumentation-fs': { enabled: false }, + '@opentelemetry/instrumentation-dns': { enabled: false }, + '@opentelemetry/instrumentation-net': { enabled: false }, + }, +}); + // Turn off safety checks for evaluation so that the LLM as an evaluator can // respond appropriately to potentially harmful content without error. export const PERMISSIVE_SAFETY_SETTINGS: any = { @@ -77,8 +98,20 @@ export const ai = genkit({ location: 'us-central1', }), vertexAIModelGarden({ - location: 'us-central1', - models: [claude35Sonnet], + location: 'us-central1', // gemini, llama + // location: 'us-east5', // anthropic + models: [ + claude35Sonnet, + claude35SonnetV2, + claude3Haiku, + claude3Opus, + claude3Sonnet, + codestral, + llama31, + llama32, + mistralLarge, + mistralNemo, + ], }), vertexAIEvaluation({ location: 'us-central1', From bae3a2c81532a18a7b9fffa6e086c25c4859223a Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Fri, 31 Jan 2025 13:02:12 -0500 Subject: [PATCH 439/562] refactor(js/ai/chat): make sendStream sync (#1732) --- js/ai/src/chat.ts | 114 ++++++++++++-------------------- js/genkit/tests/chat_test.ts | 4 +- js/genkit/tests/session_test.ts | 4 +- 3 files changed, 46 insertions(+), 76 deletions(-) diff --git a/js/ai/src/chat.ts b/js/ai/src/chat.ts index 99247cb18..292830ac3 100644 --- a/js/ai/src/chat.ts +++ b/js/ai/src/chat.ts @@ -15,6 +15,7 @@ */ import { StreamingCallback, z } from '@genkit-ai/core'; +import { Channel } from '@genkit-ai/core/async'; import { ATTR_PREFIX, SPAN_TYPE_ATTR, @@ -30,7 +31,6 @@ import { MessageData, Part, generate, - generateStream, } from './index.js'; import { BaseGenerateOptions, @@ -154,23 +154,12 @@ export class Chat { }, }, async (metadata) => { - let resolvedOptions: ChatGenerateOptions; + let resolvedOptions = resolveSendOptions(options); let streamingCallback: | StreamingCallback | undefined = undefined; - // string - if (typeof options === 'string') { - resolvedOptions = { - prompt: options, - } as ChatGenerateOptions; - } else if (Array.isArray(options)) { - // Part[] - resolvedOptions = { - prompt: options, - } as ChatGenerateOptions; - } else { - resolvedOptions = options as ChatGenerateOptions; + if (resolvedOptions.onChunk || resolvedOptions.streamingCallback) { streamingCallback = resolvedOptions.onChunk ?? resolvedOptions.streamingCallback; } @@ -204,66 +193,23 @@ export class Chat { CustomOptions extends z.ZodTypeAny = typeof GenerationCommonConfigSchema, >( options: string | Part[] | GenerateStreamOptions - ): Promise>> { - return runWithSession(this.session.registry, this.session, () => - runInNewSpan( - this.session.registry, - { - metadata: { name: 'send' }, - labels: { - [SPAN_TYPE_ATTR]: 'helper', - [SESSION_ID_ATTR]: this.session.id, - [THREAD_NAME_ATTR]: this.threadName, - }, - }, - async (metadata) => { - let resolvedOptions; - - // string - if (typeof options === 'string') { - resolvedOptions = { - prompt: options, - } as GenerateStreamOptions; - } else if (Array.isArray(options)) { - // Part[] - resolvedOptions = { - prompt: options, - } as GenerateStreamOptions; - } else { - resolvedOptions = options as GenerateStreamOptions< - O, - CustomOptions - >; - } - metadata.input = resolvedOptions; - - const { response, stream } = await generateStream( - this.session.registry, - { - ...(await this.requestBase), - messages: this.messages, - ...resolvedOptions, - } - ); + ): GenerateStreamResponse> { + let channel = new Channel(); + let resolvedOptions = resolveSendOptions(options); - return { - response: response.finally(async () => { - const resolvedResponse = await response; - this.requestBase = Promise.resolve({ - ...(await this.requestBase), - // these things may get changed by tools calling within generate. - tools: resolvedResponse?.request?.tools, - toolChoice: resolvedResponse?.request?.toolChoice, - config: resolvedResponse?.request?.config, - }); - this.updateMessages(resolvedResponse.messages); - metadata.output = JSON.stringify(resolvedResponse); - }), - stream, - }; - } - ) + const sent = this.send({ + ...resolvedOptions, + onChunk: (chunk) => channel.send(chunk), + }); + sent.then( + () => channel.close(), + (err) => channel.error(err) ); + + return { + response: sent, + stream: channel, + }; } get messages(): MessageData[] { @@ -287,3 +233,27 @@ function getPreamble(msgs?: MessageData[]) { function stripPreamble(msgs?: MessageData[]) { return msgs?.filter((m) => !m.metadata?.preamble); } + +function resolveSendOptions< + O extends z.ZodTypeAny, + CustomOptions extends z.ZodTypeAny, +>( + options: string | Part[] | ChatGenerateOptions +): ChatGenerateOptions { + let resolvedOptions: ChatGenerateOptions; + + // string + if (typeof options === 'string') { + resolvedOptions = { + prompt: options, + } as ChatGenerateOptions; + } else if (Array.isArray(options)) { + // Part[] + resolvedOptions = { + prompt: options, + } as ChatGenerateOptions; + } else { + resolvedOptions = options as ChatGenerateOptions; + } + return resolvedOptions; +} diff --git a/js/genkit/tests/chat_test.ts b/js/genkit/tests/chat_test.ts index d83bf4f3b..501ede1b3 100644 --- a/js/genkit/tests/chat_test.ts +++ b/js/genkit/tests/chat_test.ts @@ -66,7 +66,7 @@ describe('chat', () => { it('maintains history in the session with streaming', async () => { const chat = ai.chat(); - let { response, stream } = await chat.sendStream('hi'); + let { response, stream } = chat.sendStream('hi'); let chunks: string[] = []; for await (const chunk of stream) { @@ -75,7 +75,7 @@ describe('chat', () => { assert.strictEqual((await response).text, 'Echo: hi; config: {}'); assert.deepStrictEqual(chunks, ['3', '2', '1']); - ({ response, stream } = await chat.sendStream('bye')); + ({ response, stream } = chat.sendStream('bye')); chunks = []; for await (const chunk of stream) { diff --git a/js/genkit/tests/session_test.ts b/js/genkit/tests/session_test.ts index 48db6f384..684ccf300 100644 --- a/js/genkit/tests/session_test.ts +++ b/js/genkit/tests/session_test.ts @@ -170,7 +170,7 @@ describe('session', () => { const session = ai.createSession(); const chat = session.chat(); - let { response, stream } = await chat.sendStream('hi'); + let { response, stream } = chat.sendStream('hi'); let chunks: string[] = []; for await (const chunk of stream) { @@ -179,7 +179,7 @@ describe('session', () => { assert.strictEqual((await response).text, 'Echo: hi; config: {}'); assert.deepStrictEqual(chunks, ['3', '2', '1']); - ({ response, stream } = await chat.sendStream('bye')); + ({ response, stream } = chat.sendStream('bye')); chunks = []; for await (const chunk of stream) { From d2c0617736099949b7f5907cd1618bd8065b97d3 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Fri, 31 Jan 2025 14:14:03 -0500 Subject: [PATCH 440/562] fix(js/ai/prompts): fix prompt loading on node 18 (#1738) --- js/ai/src/prompt.ts | 72 ++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/js/ai/src/prompt.ts b/js/ai/src/prompt.ts index 506a684c1..58efc1ad7 100644 --- a/js/ai/src/prompt.ts +++ b/js/ai/src/prompt.ts @@ -662,36 +662,48 @@ export function loadPromptFolder( ): void { const promptsPath = resolve(dir); if (existsSync(promptsPath)) { - const dirEnts = readdirSync(promptsPath, { - withFileTypes: true, - recursive: true, - }); - for (const dirEnt of dirEnts) { - const parentPath = dirEnt.parentPath ?? dirEnt.path; - if (dirEnt.isFile() && dirEnt.name.endsWith('.prompt')) { - if (dirEnt.name.startsWith('_')) { - const partialName = dirEnt.name.substring(1, dirEnt.name.length - 7); - definePartial( - registry, - partialName, - readFileSync(join(parentPath, dirEnt.name), { - encoding: 'utf8', - }) - ); - logger.debug( - `Registered Dotprompt partial "${partialName}" from "${join(parentPath, dirEnt.name)}"` - ); - } else { - // If this prompt is in a subdirectory, we need to include that - // in the namespace to prevent naming conflicts. - let prefix = ''; - let name = dirEnt.name; - if (promptsPath !== parentPath) { - prefix = parentPath.replace(`${promptsPath}/`, '') + '/'; - } - loadPrompt(registry, promptsPath, name, prefix, ns); - } + loadPromptFolderRecursively(registry, dir, ns, ''); + } +} +export function loadPromptFolderRecursively( + registry: Registry, + dir: string, + ns: string, + subDir: string +): void { + const promptsPath = resolve(dir); + const dirEnts = readdirSync(join(promptsPath, subDir), { + withFileTypes: true, + }); + for (const dirEnt of dirEnts) { + const parentPath = join(promptsPath, subDir); + let fileName = dirEnt.name; + if (dirEnt.isFile() && fileName.endsWith('.prompt')) { + if (fileName.startsWith('_')) { + const partialName = fileName.substring(1, fileName.length - 7); + definePartial( + registry, + partialName, + readFileSync(join(parentPath, fileName), { + encoding: 'utf8', + }) + ); + logger.debug( + `Registered Dotprompt partial "${partialName}" from "${join(parentPath, fileName)}"` + ); + } else { + // If this prompt is in a subdirectory, we need to include that + // in the namespace to prevent naming conflicts. + loadPrompt( + registry, + promptsPath, + fileName, + subDir ? `${subDir}/` : '', + ns + ); } + } else if (dirEnt.isDirectory()) { + loadPromptFolderRecursively(registry, dir, ns, join(subDir, fileName)); } } } @@ -726,7 +738,7 @@ function loadPrompt( name = parts[0]; variant = parts[1]; } - const source = readFileSync(join(path, (prefix ?? '') + filename), 'utf8'); + const source = readFileSync(join(path, prefix ?? '', filename), 'utf8'); const parsedPrompt = registry.dotprompt.parse(source); definePromptAsync( registry, From b52ec840d693fc3e4af6aa0f13159d582e55d8d8 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Fri, 31 Jan 2025 11:34:25 -0800 Subject: [PATCH 441/562] feat(js/ai): Adds "restart" for tool interrupts with flexible resuming. (#1693) --- js/ai/src/generate.ts | 81 ++-- js/ai/src/generate/action.ts | 25 +- js/ai/src/generate/resolve-tool-requests.ts | 395 +++++++++++++++++--- js/ai/src/model.ts | 8 + js/ai/src/tool.ts | 84 ++++- js/ai/tests/generate/generate_test.ts | 99 ----- js/ai/tests/tool_test.ts | 77 ++++ js/core/src/action.ts | 7 +- js/genkit/tests/generate_test.ts | 187 ++++++++- js/plugins/next/src/index.ts | 6 +- js/pnpm-lock.yaml | 22 +- 11 files changed, 755 insertions(+), 236 deletions(-) diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index feee4433c..13da1c66c 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -22,7 +22,6 @@ import { runWithContext, runWithStreamingCallback, sentinelNoopStreamingCallback, - stripUndefinedProps, z, } from '@genkit-ai/core'; import { Channel } from '@genkit-ai/core/async'; @@ -46,6 +45,7 @@ import { ModelArgument, ModelMiddleware, Part, + ToolRequestPart, ToolResponsePart, resolveModel, } from './model.js'; @@ -75,7 +75,16 @@ export interface ResumeOptions { * Tools have a `.reply` helper method to construct a reply ToolResponse and validate * the data against its schema. Call `myTool.reply(interruptToolRequest, yourReplyData)`. */ - reply: ToolResponsePart | ToolResponsePart[]; + reply?: ToolResponsePart | ToolResponsePart[]; + /** + * restart will run a tool again with additionally supplied metadata passed through as + * a `resumed` option in the second argument. This allows for scenarios like conditionally + * requesting confirmation of an LLM's tool request. + * + * Tools have a `.restart` helper method to construct a restart ToolRequest. Call + * `myTool.restart(interruptToolRequest, resumeMetadata)`. + */ + restart?: ToolRequestPart | ToolRequestPart[]; /** Additional metadata to annotate the created tool message with in the "resume" key. */ metadata?: Record; } @@ -141,53 +150,6 @@ export interface GenerateOptions< context?: ActionContext; } -/** Amends message history to handle `resume` arguments. Returns the amended history. */ -async function applyResumeOption( - options: GenerateOptions, - messages: MessageData[] -): Promise { - if (!options.resume) return messages; - if ( - messages.at(-1)?.role !== 'model' || - !messages - .at(-1) - ?.content.find((p) => p.toolRequest && p.metadata?.interrupt) - ) { - throw new GenkitError({ - status: 'FAILED_PRECONDITION', - message: `Cannot 'resume' generation unless the previous message is a model message with at least one interrupt.`, - }); - } - const lastModelMessage = messages.at(-1)!; - const toolRequests = lastModelMessage.content.filter((p) => !!p.toolRequest); - - const pendingResponses: ToolResponsePart[] = toolRequests - .filter((t) => !!t.metadata?.pendingOutput) - .map((t) => - stripUndefinedProps({ - toolResponse: { - name: t.toolRequest!.name, - ref: t.toolRequest!.ref, - output: t.metadata!.pendingOutput, - }, - metadata: { source: 'pending' }, - }) - ) as ToolResponsePart[]; - - const reply = Array.isArray(options.resume.reply) - ? options.resume.reply - : [options.resume.reply]; - - const message: MessageData = { - role: 'tool', - content: [...pendingResponses, ...reply], - metadata: { - resume: options.resume.metadata || true, - }, - }; - return [...messages, message]; -} - export async function toGenerateRequest( registry: Registry, options: GenerateOptions @@ -202,8 +164,6 @@ export async function toGenerateRequest( if (options.messages) { messages.push(...options.messages.map((m) => Message.parseData(m))); } - // resuming from interrupts occurs after message history but before user prompt - messages = await applyResumeOption(options, messages); if (options.prompt) { messages.push({ role: 'user', @@ -216,6 +176,19 @@ export async function toGenerateRequest( message: 'at least one message is required in generate request', }); } + if ( + options.resume && + !( + messages.at(-1)?.role === 'model' && + messages.at(-1)?.content.find((p) => !!p.toolRequest) + ) + ) { + throw new GenkitError({ + status: 'FAILED_PRECONDITION', + message: `Last message must be a 'model' role with at least one tool request to 'resume' generation.`, + detail: messages.at(-1), + }); + } let tools: Action[] | undefined; if (options.tools) { tools = await resolveTools(registry, options.tools); @@ -386,6 +359,12 @@ export async function generate< format: resolvedOptions.output.format, jsonSchema: resolvedSchema, }, + // coerce reply and restart into arrays for the action schema + resume: resolvedOptions.resume && { + reply: [resolvedOptions.resume.reply || []].flat(), + restart: [resolvedOptions.resume.restart || []].flat(), + metadata: resolvedOptions.resume.metadata, + }, returnToolRequests: resolvedOptions.returnToolRequests, maxTurns: resolvedOptions.maxTurns, }; diff --git a/js/ai/src/generate/action.ts b/js/ai/src/generate/action.ts index 41d84006e..0b694835c 100644 --- a/js/ai/src/generate/action.ts +++ b/js/ai/src/generate/action.ts @@ -17,6 +17,7 @@ import { Action, defineAction, + GenkitError, getStreamingCallback, runWithStreamingCallback, stripUndefinedProps, @@ -25,7 +26,7 @@ import { import { logger } from '@genkit-ai/core/logging'; import { Registry } from '@genkit-ai/core/registry'; import { toJsonSchema } from '@genkit-ai/core/schema'; -import { SPAN_TYPE_ATTR, runInNewSpan } from '@genkit-ai/core/tracing'; +import { runInNewSpan, SPAN_TYPE_ATTR } from '@genkit-ai/core/tracing'; import { injectInstructions, resolveFormat, @@ -52,12 +53,13 @@ import { ModelMiddleware, ModelRequest, Part, - Role, resolveModel, + Role, } from '../model.js'; -import { ToolAction, resolveTools, toToolDefinition } from '../tool.js'; +import { resolveTools, ToolAction, toToolDefinition } from '../tool.js'; import { assertValidToolNames, + resolveResumeOption, resolveToolRequests, } from './resolve-tool-requests.js'; @@ -225,6 +227,23 @@ async function generate( // check to make sure we don't have overlapping tool names *before* generation await assertValidToolNames(tools); + const { revisedRequest, interruptedResponse } = await resolveResumeOption( + registry, + rawRequest + ); + // NOTE: in the future we should make it possible to interrupt a restart, but + // at the moment it's too complicated because it's not clear how to return a + // response that amends history but doesn't generate a new message, so we throw + if (interruptedResponse) { + throw new GenkitError({ + status: 'FAILED_PRECONDITION', + message: + 'One or more tools triggered an interrupt during a restarted execution.', + detail: { message: interruptedResponse.message }, + }); + } + rawRequest = revisedRequest!; + const request = await actionToGenerateRequest( rawRequest, tools, diff --git a/js/ai/src/generate/resolve-tool-requests.ts b/js/ai/src/generate/resolve-tool-requests.ts index c8ead6e28..220350f70 100644 --- a/js/ai/src/generate/resolve-tool-requests.ts +++ b/js/ai/src/generate/resolve-tool-requests.ts @@ -19,11 +19,20 @@ import { logger } from '@genkit-ai/core/logging'; import { Registry } from '@genkit-ai/core/registry'; import { GenerateActionOptions, + GenerateResponseData, MessageData, + Part, + ToolRequestPart, ToolResponsePart, } from '../model.js'; import { isPromptAction } from '../prompt.js'; -import { ToolAction, ToolInterruptError, resolveTools } from '../tool.js'; +import { + ToolAction, + ToolInterruptError, + ToolRunOptions, + isToolRequest, + resolveTools, +} from '../tool.js'; export function toToolMap(tools: ToolAction[]): Record { assertValidToolNames(tools); @@ -52,6 +61,87 @@ export function assertValidToolNames(tools: ToolAction[]) { } } +function toRunOptions(part: ToolRequestPart): ToolRunOptions { + const out: ToolRunOptions = { metadata: part.metadata }; + if (part.metadata?.resumed) out.resumed = part.metadata.resumed; + return out; +} + +export function toPendingOutput( + part: ToolRequestPart, + response: ToolResponsePart +): ToolRequestPart { + return { + ...part, + metadata: { + ...part.metadata, + pendingOutput: response.toolResponse.output, + }, + }; +} + +export async function resolveToolRequest( + rawRequest: GenerateActionOptions, + part: ToolRequestPart, + toolMap: Record, + runOptions?: ToolRunOptions +): Promise<{ + response?: ToolResponsePart; + interrupt?: ToolRequestPart; + preamble?: GenerateActionOptions; +}> { + const tool = toolMap[part.toolRequest.name]; + if (!tool) { + throw new GenkitError({ + status: 'NOT_FOUND', + message: `Tool ${part.toolRequest.name} not found`, + detail: { request: rawRequest }, + }); + } + + // if it's a prompt action, go ahead and render the preamble + if (isPromptAction(tool)) { + const preamble = await tool(part.toolRequest.input); + const response = { + toolResponse: { + name: part.toolRequest.name, + ref: part.toolRequest.ref, + output: `transferred to ${part.toolRequest.name}`, + }, + }; + + return { preamble, response }; + } + + // otherwise, execute the tool and catch interrupts + try { + const output = await tool(part.toolRequest.input, toRunOptions(part)); + const response = stripUndefinedProps({ + toolResponse: { + name: part.toolRequest.name, + ref: part.toolRequest.ref, + output, + }, + }); + + return { response }; + } catch (e) { + if (e instanceof ToolInterruptError) { + logger.debug( + `tool '${toolMap[part.toolRequest?.name].__action.name}' triggered an interrupt${e.metadata ? `: ${JSON.stringify(e.metadata)}` : ''}` + ); + const interrupt = { + toolRequest: part.toolRequest, + metadata: { ...part.metadata, interrupt: e.metadata || true }, + }; + + return { interrupt }; + } + + throw e; + } +} + /** * resolveToolRequests is responsible for executing the tools requested by the model for a single turn. it * returns either a toolMessage to append or a revisedModelMessage when an interrupt occurs, and a transferPreamble @@ -81,66 +171,36 @@ export async function resolveToolRequests( revisedModelMessage.content.map(async (part, i) => { if (!part.toolRequest) return; // skip non-tool-request parts - const tool = toolMap[part.toolRequest.name]; - if (!tool) { - throw new GenkitError({ - status: 'NOT_FOUND', - message: `Tool ${part.toolRequest.name} not found`, - detail: { request: rawRequest }, - }); - } + const { preamble, response, interrupt } = await resolveToolRequest( + rawRequest, + part as ToolRequestPart, + toolMap + ); - // if it's a prompt action, go ahead and render the preamble - if (isPromptAction(tool)) { - if (transferPreamble) + if (preamble) { + if (transferPreamble) { throw new GenkitError({ status: 'INVALID_ARGUMENT', message: `Model attempted to transfer to multiple prompt tools.`, }); - transferPreamble = await tool(part.toolRequest.input); - responseParts.push({ - toolResponse: { - name: part.toolRequest.name, - ref: part.toolRequest.ref, - output: `transferred to ${part.toolRequest.name}`, - }, - }); - return; + } + + transferPreamble = preamble; } - // otherwise, execute the tool and catch interrupts - try { - const output = await tool(part.toolRequest.input, {}); - const responsePart = stripUndefinedProps({ - toolResponse: { - name: part.toolRequest.name, - ref: part.toolRequest.ref, - output, - }, - }); - - revisedModelMessage.content.splice(i, 1, { - ...part, - metadata: { - ...part.metadata, - pendingOutput: responsePart.toolResponse.output, - }, - }); - responseParts.push(responsePart); - } catch (e) { - if (e instanceof ToolInterruptError) { - logger.debug( - `tool '${toolMap[part.toolRequest?.name].__action.name}' triggered an interrupt${e.metadata ? `: ${JSON.stringify(e.metadata)}` : ''}` - ); - revisedModelMessage.content.splice(i, 1, { - toolRequest: part.toolRequest, - metadata: { ...part.metadata, interrupt: e.metadata || true }, - }); - hasInterrupts = true; - return; - } + // this happens for preamble or normal tools + if (response) { + responseParts.push(response!); + revisedModelMessage.content.splice( + i, + 1, + toPendingOutput(part, response) + ); + } - throw e; + if (interrupt) { + revisedModelMessage.content.splice(i, 1, interrupt); + hasInterrupts = true; } }) ); @@ -154,3 +214,232 @@ export async function resolveToolRequests( transferPreamble, }; } + +function findCorrespondingToolRequest( + parts: Part[], + part: ToolRequestPart | ToolResponsePart +): ToolRequestPart | undefined { + const name = part.toolRequest?.name || part.toolResponse?.name; + const ref = part.toolRequest?.ref || part.toolResponse?.ref; + + return parts.find( + (p) => p.toolRequest?.name === name && p.toolRequest?.ref === ref + ) as ToolRequestPart | undefined; +} + +function findCorrespondingToolResponse( + parts: Part[], + part: ToolRequestPart | ToolResponsePart +): ToolResponsePart | undefined { + const name = part.toolRequest?.name || part.toolResponse?.name; + const ref = part.toolRequest?.ref || part.toolResponse?.ref; + + return parts.find( + (p) => p.toolResponse?.name === name && p.toolResponse?.ref === ref + ) as ToolResponsePart | undefined; +} + +async function resolveResumedToolRequest( + rawRequest: GenerateActionOptions, + part: ToolRequestPart, + toolMap: Record +): Promise<{ + toolRequest?: ToolRequestPart; + toolResponse?: ToolResponsePart; + interrupt?: ToolRequestPart; +}> { + if (part.metadata?.pendingOutput) { + const { pendingOutput, ...metadata } = part.metadata; + const toolResponse = { + toolResponse: { + name: part.toolRequest.name, + ref: part.toolRequest.ref, + output: pendingOutput, + }, + metadata: { ...metadata, source: 'pending' }, + }; + + // strip pendingOutput from metadata when returning + return stripUndefinedProps({ + toolResponse, + toolRequest: { ...part, metadata }, + }); + } + + // if there's a corresponding reply, append it to toolResponses + const replyResponse = findCorrespondingToolResponse( + rawRequest.resume?.reply || [], + part + ); + if (replyResponse) { + const toolResponse = replyResponse; + + // remove the 'interrupt' but leave a 'resolvedInterrupt' + const { interrupt, ...metadata } = part.metadata || {}; + return stripUndefinedProps({ + toolResponse, + toolRequest: { + ...part, + metadata: { ...metadata, resolvedInterrupt: interrupt }, + }, + }); + } + + // if there's a corresponding restart, execute then add to toolResponses + const restartRequest = findCorrespondingToolRequest( + rawRequest.resume?.restart || [], + part + ); + if (restartRequest) { + const { response, interrupt, preamble } = await resolveToolRequest( + rawRequest, + restartRequest, + toolMap + ); + + if (preamble) { + throw new GenkitError({ + status: 'INTERNAL', + message: `Prompt tool '${restartRequest.toolRequest.name}' executed inside 'restart' resolution. This should never happen.`, + }); + } + + // if there's a new interrupt, return it + if (interrupt) return { interrupt }; + + if (response) { + const toolResponse = response; + + // remove the 'interrupt' but leave a 'resolvedInterrupt' + const { interrupt, ...metadata } = part.metadata || {}; + return stripUndefinedProps({ + toolResponse, + toolRequest: { + ...part, + metadata: { ...metadata, resolvedInterrupt: interrupt }, + }, + }); + } + } + + throw new GenkitError({ + status: 'INVALID_ARGUMENT', + message: `Unresolved tool request '${part.toolRequest.name}${part.toolRequest.ref ? `#${part.toolRequest.ref}` : ''} was not handled by the 'resume' argument. You must supply replies or restarts for all interrupted tool requests.'`, + }); +} + +/** Amends message history to handle `resume` arguments. Returns the amended history. */ +export async function resolveResumeOption( + registry: Registry, + rawRequest: GenerateActionOptions +): Promise<{ + revisedRequest?: GenerateActionOptions; + interruptedResponse?: GenerateResponseData; +}> { + if (!rawRequest.resume) return { revisedRequest: rawRequest }; // no-op if no resume option + console.log('RESOLVE RESUME OPTION:', rawRequest.resume); + const toolMap = toToolMap(await resolveTools(registry, rawRequest.tools)); + + const messages = rawRequest.messages; + const lastMessage = messages.at(-1); + + if ( + !lastMessage || + lastMessage.role !== 'model' || + !lastMessage.content.find((p) => p.toolRequest && p.metadata?.interrupt) + ) { + throw new GenkitError({ + status: 'FAILED_PRECONDITION', + message: `Cannot 'resume' generation unless the previous message is a model message with at least one interrupt.`, + }); + } + + const toolResponses: ToolResponsePart[] = []; + let interrupted = false; + + lastMessage.content = await Promise.all( + lastMessage.content.map(async (part) => { + if (!isToolRequest(part)) return part; + const resolved = await resolveResumedToolRequest( + rawRequest, + part, + toolMap + ); + console.log('RESOLVED TOOL', part.toolRequest.name, 'TO', resolved); + if (resolved.interrupt) { + interrupted = true; + return resolved.interrupt; + } + + toolResponses.push(resolved.toolResponse!); + return resolved.toolRequest!; + }) + ); + + if (interrupted) { + // TODO: figure out how to make this trigger an interrupt response. + return { + interruptedResponse: { + finishReason: 'interrupted', + finishMessage: + 'One or more tools triggered interrupts while resuming generation. The model was not called.', + message: lastMessage, + }, + }; + } + + const numToolRequests = lastMessage.content.filter( + (p) => !!p.toolRequest + ).length; + if (toolResponses.length !== numToolRequests) { + throw new GenkitError({ + status: 'FAILED_PRECONDITION', + message: `Expected ${numToolRequests} tool responses but resolved to ${toolResponses.length}.`, + detail: { toolResponses, message: lastMessage }, + }); + } + + const toolMessage: MessageData = { + role: 'tool', + content: toolResponses, + metadata: { + resumed: rawRequest.resume.metadata || true, + }, + }; + + console.log('CONSTRUCTED A TOOL MESSAGE:', toolMessage.content); + return stripUndefinedProps({ + revisedRequest: { + ...rawRequest, + resume: undefined, + messages: [...messages, toolMessage], + }, + }); +} + +export async function resolveRestartedTools( + registry: Registry, + rawRequest: GenerateActionOptions +): Promise { + const toolMap = toToolMap(await resolveTools(registry, rawRequest.tools)); + const lastMessage = rawRequest.messages.at(-1); + if (!lastMessage || lastMessage.role !== 'model') return []; + + const restarts = lastMessage.content.filter( + (p) => p.toolRequest && p.metadata?.resumed + ) as ToolRequestPart[]; + + return await Promise.all( + restarts.map(async (p) => { + const { response, interrupt } = await resolveToolRequest( + rawRequest, + p, + toolMap + ); + + // this means that it interrupted *again* after the restart + if (interrupt) return interrupt; + return toPendingOutput(p, response!); + }) + ); +} diff --git a/js/ai/src/model.ts b/js/ai/src/model.ts index 284f285fd..b2a8884c0 100644 --- a/js/ai/src/model.ts +++ b/js/ai/src/model.ts @@ -711,6 +711,14 @@ export const GenerateActionOptionsSchema = z.object({ jsonSchema: z.any().optional(), }) .optional(), + /** Options for resuming an interrupted generation. */ + resume: z + .object({ + reply: z.array(ToolResponsePartSchema).optional(), + restart: z.array(ToolRequestPartSchema).optional(), + metadata: z.record(z.any()).optional(), + }) + .optional(), /** When true, return tool calls for manual processing instead of automatically resolving them. */ returnToolRequests: z.boolean().optional(), /** Maximum number of tool call iterations that can be performed in a single generate call (default 5). */ diff --git a/js/ai/src/tool.ts b/js/ai/src/tool.ts index a39cc96da..722a62590 100644 --- a/js/ai/src/tool.ts +++ b/js/ai/src/tool.ts @@ -17,6 +17,7 @@ import { Action, ActionContext, + ActionRunOptions, defineAction, JSONSchema7, stripUndefinedProps, @@ -25,7 +26,12 @@ import { import { Registry } from '@genkit-ai/core/registry'; import { parseSchema, toJsonSchema } from '@genkit-ai/core/schema'; import { setCustomMetadataAttributes } from '@genkit-ai/core/tracing'; -import { ToolDefinition, ToolRequestPart, ToolResponsePart } from './model.js'; +import { + Part, + ToolDefinition, + ToolRequestPart, + ToolResponsePart, +} from './model.js'; import { ExecutablePrompt } from './prompt.js'; /** @@ -34,7 +40,7 @@ import { ExecutablePrompt } from './prompt.js'; export type ToolAction< I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, -> = Action & { +> = Action & { __action: { metadata: { type: 'tool'; @@ -55,8 +61,37 @@ export type ToolAction< replyData: z.infer, options?: { metadata?: Record } ): ToolResponsePart; + /** + * restart constructs a tool request corresponding to the provided interrupt tool request + * that will then re-trigger the tool after e.g. a user confirms. The `resumedMetadata` + * supplied to this method will be passed to the tool to allow for custom handling of + * restart logic. + * + * @param interrupt The interrupt tool request you want to restart. + * @param resumedMetadata The metadata you want to provide to the tool to aide in reprocessing. Defaults to `true` if none is supplied. + * @param options Additional options for restarting the tool. + */ + restart( + interrupt: ToolRequestPart, + resumedMetadata?: any, + options?: { + /** + * Replace the existing input arguments to the tool with different ones, for example + * if the user revised an action before confirming. When input is replaced, the existing + * tool request will be amended in the message history. + **/ + replaceInput?: z.infer; + } + ): ToolRequestPart; }; +export interface ToolRunOptions extends ActionRunOptions { + /** If resumed is supplied to a tool at runtime, that means that it was previously interrupted and this is a second */ + resumed?: boolean | Record; + /** The metadata from the tool request that triggered this run. */ + metadata?: Record; +} + /** * Configuration for a tool. */ @@ -200,7 +235,7 @@ export interface ToolFnOptions { export type ToolFn = ( input: z.infer, - ctx: ToolFnOptions + ctx: ToolFnOptions & ToolRunOptions ) => Promise>; /** @@ -220,13 +255,13 @@ export function defineTool( actionType: 'tool', metadata: { ...(config.metadata || {}), type: 'tool' }, }, - (i, { context }) => - fn(i, { + (i, runOptions) => { + return fn(i, { + ...runOptions, + context: { ...runOptions.context }, interrupt: interruptTool, - context: { - ...context, - }, - }) + }); + } ); (a as ToolAction).reply = (interrupt, replyData, options) => { parseSchema(replyData, { @@ -244,6 +279,29 @@ export function defineTool( }, }; }; + + (a as ToolAction).restart = (interrupt, resumedMetadata, options) => { + let replaceInput = options?.replaceInput; + if (replaceInput) { + replaceInput = parseSchema(replaceInput, { + schema: config.inputSchema, + jsonSchema: config.inputJsonSchema, + }); + } + return { + toolRequest: stripUndefinedProps({ + name: interrupt.toolRequest.name, + ref: interrupt.toolRequest.ref, + input: replaceInput || interrupt.toolRequest.input, + }), + metadata: stripUndefinedProps({ + ...interrupt.metadata, + resumed: resumedMetadata || true, + // annotate the original input if replacing it + replacedInput: replaceInput ? interrupt.toolRequest.input : undefined, + }), + }; + }; return a as ToolAction; } @@ -264,6 +322,14 @@ export type InterruptConfig< ) => Record | Promise>); }; +export function isToolRequest(part: Part): part is ToolRequestPart { + return !!part.toolRequest; +} + +export function isToolResponse(part: Part): part is ToolResponsePart { + return !!part.toolResponse; +} + export function defineInterrupt( registry: Registry, config: InterruptConfig diff --git a/js/ai/tests/generate/generate_test.ts b/js/ai/tests/generate/generate_test.ts index ac5399a83..085e69070 100644 --- a/js/ai/tests/generate/generate_test.ts +++ b/js/ai/tests/generate/generate_test.ts @@ -279,105 +279,6 @@ describe('toGenerateRequest', () => { }, throws: 'FAILED_PRECONDITION', }, - { - should: 'add pending responses and interrupt replies to a tool message', - prompt: { - messages: [ - { role: 'user', content: [{ text: 'hey' }] }, - { - role: 'model', - content: [ - { - toolRequest: { name: 'p1', ref: '1', input: { one: '1' } }, - metadata: { - pendingOutput: 'done', - }, - }, - { - toolRequest: { name: 'p2', ref: '2', input: { one: '1' } }, - metadata: { - pendingOutput: 'done2', - }, - }, - { - toolRequest: { name: 'i1', ref: '3', input: { one: '1' } }, - metadata: { - interrupt: true, - }, - }, - { - toolRequest: { name: 'i2', ref: '4', input: { one: '1' } }, - metadata: { - interrupt: { sky: 'blue' }, - }, - }, - ], - }, - ], - resume: { - reply: [ - { toolResponse: { name: 'i1', ref: '3', output: 'done3' } }, - { toolResponse: { name: 'i2', ref: '4', output: 'done4' } }, - ], - }, - }, - expectedOutput: { - config: undefined, - docs: undefined, - output: {}, - tools: [], - messages: [ - { role: 'user', content: [{ text: 'hey' }] }, - { - role: 'model', - content: [ - { - toolRequest: { name: 'p1', ref: '1', input: { one: '1' } }, - metadata: { - pendingOutput: 'done', - }, - }, - { - toolRequest: { name: 'p2', ref: '2', input: { one: '1' } }, - metadata: { - pendingOutput: 'done2', - }, - }, - { - toolRequest: { name: 'i1', ref: '3', input: { one: '1' } }, - metadata: { - interrupt: true, - }, - }, - { - toolRequest: { name: 'i2', ref: '4', input: { one: '1' } }, - metadata: { - interrupt: { sky: 'blue' }, - }, - }, - ], - }, - { - role: 'tool', - metadata: { - resume: true, - }, - content: [ - { - toolResponse: { name: 'p1', ref: '1', output: 'done' }, - metadata: { source: 'pending' }, - }, - { - toolResponse: { name: 'p2', ref: '2', output: 'done2' }, - metadata: { source: 'pending' }, - }, - { toolResponse: { name: 'i1', ref: '3', output: 'done3' } }, - { toolResponse: { name: 'i2', ref: '4', output: 'done4' } }, - ], - }, - ], - }, - }, ]; for (const test of testCases) { it(test.should, async () => { diff --git a/js/ai/tests/tool_test.ts b/js/ai/tests/tool_test.ts index e1ac74098..3d184a517 100644 --- a/js/ai/tests/tool_test.ts +++ b/js/ai/tests/tool_test.ts @@ -183,4 +183,81 @@ describe('defineTool', () => { ); }); }); + + describe('.restart()', () => { + it('constructs a ToolRequestPart', () => { + const t = defineTool( + registry, + { name: 'test', description: 'test' }, + async () => {} + ); + assert.deepStrictEqual( + t.restart({ toolRequest: { name: 'test', input: {} } }), + { + toolRequest: { + name: 'test', + input: {}, + }, + metadata: { + resumed: true, + }, + } + ); + }); + + it('includes metadata', () => { + const t = defineTool( + registry, + { name: 'test', description: 'test' }, + async () => {} + ); + assert.deepStrictEqual( + t.restart( + { toolRequest: { name: 'test', input: {} } }, + { extra: 'data' } + ), + { + toolRequest: { + name: 'test', + input: {}, + }, + metadata: { + resumed: { extra: 'data' }, + }, + } + ); + }); + + it('validates schema', () => { + const t = defineTool( + registry, + { name: 'test', description: 'test', inputSchema: z.number() }, + async (input, { interrupt }) => interrupt() + ); + assert.throws( + () => { + t.restart({ toolRequest: { name: 'test', input: {} } }, undefined, { + replaceInput: 'not_a_number' as any, + }); + }, + { name: 'GenkitError', status: 'INVALID_ARGUMENT' } + ); + + assert.deepStrictEqual( + t.restart({ toolRequest: { name: 'test', input: {} } }, undefined, { + replaceInput: 55, + }), + { + toolRequest: { + name: 'test', + input: 55, + }, + metadata: { + resumed: true, + replacedInput: {}, + }, + } + ); + }); + }); }); diff --git a/js/core/src/action.ts b/js/core/src/action.ts index 1bc7362a3..7683d9893 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -114,10 +114,8 @@ export type Action< I extends z.ZodTypeAny = z.ZodTypeAny, O extends z.ZodTypeAny = z.ZodTypeAny, S extends z.ZodTypeAny = z.ZodTypeAny, -> = (( - input?: z.infer, - options?: ActionRunOptions -) => Promise>) & { + RunOptions extends ActionRunOptions = ActionRunOptions, +> = ((input?: z.infer, options?: RunOptions) => Promise>) & { __action: ActionMetadata; __registry: Registry; run( @@ -313,6 +311,7 @@ export function action< try { const actionFn = () => fn(input, { + ...options, // Context can either be explicitly set, or inherited from the parent action. context: options?.context ?? getContext(registry), sendChunk: options?.onChunk ?? sentinelNoopStreamingCallback, diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index 81285862d..4fe0278d3 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { MessageData } from '@genkit-ai/ai'; import { z } from '@genkit-ai/core'; import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; @@ -645,6 +646,13 @@ describe('generate', () => { async (input, { interrupt }) => interrupt({ confirm: 'is it a banana?' }) ); + ai.defineTool( + { name: 'resumableTool', description: 'description' }, + async (input, { interrupt, resumed }) => { + if ((resumed as any)?.status === 'ok') return true; + return interrupt(); + } + ); // first response be tools call, the subsequent just text response from agent b. let reqCounter = 0; @@ -669,7 +677,14 @@ describe('generate', () => { toolRequest: { name: 'simpleTool', input: { name: 'foo' }, - ref: 'ref123', + ref: 'ref456', + }, + }, + { + toolRequest: { + name: 'resumableTool', + input: { doIt: true }, + ref: 'ref789', }, }, ] @@ -680,7 +695,7 @@ describe('generate', () => { const response = await ai.generate({ prompt: 'call the tool', - tools: ['interruptingTool', 'simpleTool'], + tools: ['interruptingTool', 'simpleTool', 'resumableTool'], }); assert.strictEqual(reqCounter, 1); @@ -703,12 +718,24 @@ describe('generate', () => { name: 'foo', }, name: 'simpleTool', - ref: 'ref123', + ref: 'ref456', }, metadata: { pendingOutput: 'response: foo', }, }, + { + metadata: { + interrupt: true, + }, + toolRequest: { + name: 'resumableTool', + ref: 'ref789', + input: { + doIt: true, + }, + }, + }, ]); assert.deepStrictEqual(response.message?.toJSON(), { role: 'model', @@ -734,12 +761,24 @@ describe('generate', () => { name: 'foo', }, name: 'simpleTool', - ref: 'ref123', + ref: 'ref456', }, metadata: { pendingOutput: 'response: foo', }, }, + { + metadata: { + interrupt: true, + }, + toolRequest: { + name: 'resumableTool', + ref: 'ref789', + input: { + doIt: true, + }, + }, + }, ], }); assert.deepStrictEqual(pm.lastRequest, { @@ -772,8 +811,148 @@ describe('generate', () => { $schema: 'http://json-schema.org/draft-07/schema#', }, }, + { + description: 'description', + inputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + name: 'resumableTool', + outputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + }, + }, ], }); }); + + it('can resume generation', { only: true }, async () => { + const interrupter = ai.defineInterrupt({ + name: 'interrupter', + description: 'always interrupts', + }); + const truth = ai.defineTool( + { name: 'truth', description: 'always returns true' }, + async () => true + ); + const resumable = ai.defineTool( + { + name: 'resumable', + description: 'interrupts unless resumed with {status: "ok"}', + }, + async (_input, { interrupt, resumed }) => { + console.log('RESUMABLE TOOL CALLED WITH:', resumed); + if ((resumed as any)?.status === 'ok') return true; + return interrupt(); + } + ); + + const messages: MessageData[] = [ + { role: 'user', content: [{ text: 'hello' }] }, + { + role: 'model', + content: [ + { + toolRequest: { name: 'interrupter', input: {} }, + metadata: { interrupt: true }, + }, + { + toolRequest: { name: 'truth', input: {} }, + metadata: { pendingOutput: true }, + }, + { + toolRequest: { name: 'resumable', input: {} }, + metadata: { interrupt: true }, + }, + ], + }, + ]; + + const response = await ai.generate({ + model: 'echoModel', + messages, + tools: [interrupter, resumable, truth], + resume: { + reply: interrupter.reply( + { + toolRequest: { name: 'interrupter', input: {} }, + metadata: { interrupt: true }, + }, + 23 + ), + restart: resumable.restart( + { + toolRequest: { name: 'resumable', input: {} }, + metadata: { interrupt: true }, + }, + { status: 'ok' } + ), + }, + }); + + const revisedModelMessage = response.messages.at(-3); + const toolMessage = response.messages.at(-2); + + assert.deepStrictEqual( + revisedModelMessage?.content, + [ + { + metadata: { + resolvedInterrupt: true, + }, + toolRequest: { + input: {}, + name: 'interrupter', + }, + }, + { + metadata: {}, + toolRequest: { + input: {}, + name: 'truth', + }, + }, + { + metadata: { + resolvedInterrupt: true, + }, + toolRequest: { + input: {}, + name: 'resumable', + }, + }, + ], + 'resuming amends the model message to resolve interrupts' + ); + assert.deepStrictEqual( + toolMessage?.content, + [ + { + metadata: { + reply: true, + }, + toolResponse: { + name: 'interrupter', + output: 23, + }, + }, + { + metadata: { + source: 'pending', + }, + toolResponse: { + name: 'truth', + output: true, + }, + }, + { + toolResponse: { + name: 'resumable', + output: true, + }, + }, + ], + 'resuming generates a tool message containing all expected responses' + ); + }); }); }); diff --git a/js/plugins/next/src/index.ts b/js/plugins/next/src/index.ts index 4d4bb6194..8cfce3281 100644 --- a/js/plugins/next/src/index.ts +++ b/js/plugins/next/src/index.ts @@ -17,8 +17,10 @@ import type { Action } from '@genkit-ai/core'; import { NextRequest, NextResponse } from 'next/server.js'; -const appRoute = - >(action: A) => +const appRoute: >( + action: A +) => (req: NextRequest) => Promise = + (action) => async (req: NextRequest): Promise => { const { data } = await req.json(); if (req.headers.get('accept') !== 'text/event-stream') { diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 98f8ef086..2ca35d8c5 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -1373,7 +1373,7 @@ importers: version: link:../../plugins/ollama genkitx-openai: specifier: ^0.10.1 - version: 0.10.1(@genkit-ai/ai@1.0.0-rc.10)(@genkit-ai/core@1.0.0-rc.10) + version: 0.10.1(@genkit-ai/ai@1.0.0-rc.12)(@genkit-ai/core@1.0.0-rc.12) devDependencies: rimraf: specifier: ^6.0.1 @@ -2505,11 +2505,11 @@ packages: '@firebase/webchannel-wrapper@1.0.3': resolution: {integrity: sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==} - '@genkit-ai/ai@1.0.0-rc.10': - resolution: {integrity: sha512-FIwseXpGFChZAoGXJiSDFSqEUam5dK0SR6EExtukBfZg4BGLz5e0XjzqmPvf8HW3iCWF/3MOZEBx7fVrCW60+g==} + '@genkit-ai/ai@1.0.0-rc.12': + resolution: {integrity: sha512-1hofDfuTEVDfryy7klgeJwCyXOp4wEZ+VPU34Dts9S5PYoN1nb23KAhnz+6dHgM42REM1PI5Zh2Wq8O10BXIww==} - '@genkit-ai/core@1.0.0-rc.10': - resolution: {integrity: sha512-KA+z3oqOsL2w65cMchfmS1ont1gkm0WYOQwzm9cxmyjBbbGP1Ndq3udCIUJM410emITuU4wHEDu1dokDoSaOWA==} + '@genkit-ai/core@1.0.0-rc.12': + resolution: {integrity: sha512-3K7GVXR1vnJu2ANICbojxKpBoxIncuGIvn0NJ7T+s3/IOBQcusmkUyJoc4TdeRsZdX693J+oMUSx+ZE18s28eQ==} '@gerrit0/mini-shiki@1.24.4': resolution: {integrity: sha512-YEHW1QeAg6UmxEmswiQbOVEg1CW22b1XUD/lNTliOsu0LD0wqoyleFMnmbTp697QE0pcadQiR5cVtbbAPncvpw==} @@ -7822,9 +7822,9 @@ snapshots: '@firebase/webchannel-wrapper@1.0.3': {} - '@genkit-ai/ai@1.0.0-rc.10': + '@genkit-ai/ai@1.0.0-rc.12': dependencies: - '@genkit-ai/core': 1.0.0-rc.10 + '@genkit-ai/core': 1.0.0-rc.12 '@opentelemetry/api': 1.9.0 '@types/node': 20.16.9 colorette: 2.0.20 @@ -7836,7 +7836,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@genkit-ai/core@1.0.0-rc.10': + '@genkit-ai/core@1.0.0-rc.12': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) @@ -10578,10 +10578,10 @@ snapshots: - encoding - supports-color - genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-rc.10)(@genkit-ai/core@1.0.0-rc.10): + genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-rc.12)(@genkit-ai/core@1.0.0-rc.12): dependencies: - '@genkit-ai/ai': 1.0.0-rc.10 - '@genkit-ai/core': 1.0.0-rc.10 + '@genkit-ai/ai': 1.0.0-rc.12 + '@genkit-ai/core': 1.0.0-rc.12 openai: 4.53.0(encoding@0.1.13) zod: 3.24.1 transitivePeerDependencies: From 44450a356492b123b358dd0c63498200a7e4a822 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:54:51 +0000 Subject: [PATCH 442/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.13 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 161743581..58f094ef2 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From e573c1185904d5f698252dc27eea5a6c60698be6 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:54:53 +0000 Subject: [PATCH 443/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.13 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 434903fa8..0f15db6b7 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From db75aae8ae177360695914dc0804c13a04167532 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:54:56 +0000 Subject: [PATCH 444/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.13 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index c95fd1203..29b9f0198 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From 83956c190bf49b7f7d7795f7424287a046082101 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:55:32 +0000 Subject: [PATCH 445/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.13 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 0bd677a81..8dd06cfb8 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From 871ccfdd5cad61acf81c72fb81b2951dd8c6b860 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:55:34 +0000 Subject: [PATCH 446/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.13 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index 1d6e7239a..2b987ef45 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From cc14a6d93ebcf39c8b419d78a702cc6de34e0009 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:55:36 +0000 Subject: [PATCH 447/562] chore: bump genkit version to genkit@1.0.0-rc.13 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index 228679de4..f96141fb2 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From a9a10a5b8842958d8e27ed0028ef2382eb2e0eb2 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:55:39 +0000 Subject: [PATCH 448/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.13 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index 22bc91232..82f415f69 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From ffcdb0540663f209e32f8f65686f950462e600b0 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:55:41 +0000 Subject: [PATCH 449/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.13 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 273ffb844..5374f59ce 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From 0552c2b3fd62857922ea772db17aea3feb52b895 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:55:43 +0000 Subject: [PATCH 450/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.13 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index d2c2a5738..7c9697813 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From 4e27745b3eacd07f4b5816a6cec2acdf86741f97 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:55:46 +0000 Subject: [PATCH 451/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.13 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 0f91d0487..f0c19a3d5 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From 020798ea363df1b15aa19af8c36d109d0b06990c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:55:48 +0000 Subject: [PATCH 452/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.13 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index ec97e5fc1..38bb49519 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From 63f17397b098fed7d146e9c082e5f42c0323e836 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:55:50 +0000 Subject: [PATCH 453/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.13 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 07992708c..83c731978 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From 531478f5f7a47d5dafd3cf5e6a97cbed4a9ad99e Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:55:53 +0000 Subject: [PATCH 454/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.13 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index 523e51ba5..ece5f64b9 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From b7e88cb1036d3971f3f469d33d34692219bb0daa Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:55:55 +0000 Subject: [PATCH 455/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.13 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index 0dc4c21ca..03f6f52e1 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From 7985d8813ab47c24360a5921c314777a8fa1bc6d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:55:57 +0000 Subject: [PATCH 456/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.13 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index 08d10f745..88caee4bf 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From b19772b49620fecadf4746628f7d1798990b06bf Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:56:00 +0000 Subject: [PATCH 457/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.13 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 60708042c..a30186661 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From 909d07d5be67da82e93fecc308052b16a83f59eb Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:56:02 +0000 Subject: [PATCH 458/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.13 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index 64c5fe599..82ed33d76 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From d529618c6f8888bbf4afffe6541dcef29395bf7f Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:56:05 +0000 Subject: [PATCH 459/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.13 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index 75e40af48..885a9430f 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From 94b5b6c69c5808a6ef90d0ae00c58c9645e13e10 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Fri, 31 Jan 2025 19:56:07 +0000 Subject: [PATCH 460/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.13 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index 2bbfb4bec..c15a459b9 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.12", + "version": "1.0.0-rc.13", "type": "commonjs", "scripts": { "check": "tsc", From 03da015115eb0c1734fcd0a80ce69e3049747e7b Mon Sep 17 00:00:00 2001 From: Yesudeep Mangalapilly Date: Fri, 31 Jan 2025 12:58:05 -0800 Subject: [PATCH 461/562] chore(build): update copyright headers and pre-commit hook #1736 (#1740) Issue: https://github.com/firebase/genkit/issues/1736 The copyright headers in Python files have been updated to include SPDX license identifier and change "Google Inc." to "Google LLC". Additionally, simplified the pre-commit hook command for branch protection. CHANGELOG: - chore: update copyright header to use "Google LLC" instead of "Google Inc." - chore: add SPDX-License-Identifier to copyright headers - build: simplify no-commits-on-branches pre-commit hook command by removing 'py' argument - feat: add check licenses script for later use - chore: remove spurious .projectile file that would cause emacs to tighten scope - refactor: move PATH environment variable handling into its own function - feat: add govulncheck to check for known vulnerabilities in dependencies - feat: add cleanup temp files script - chore: configure ci checks to run regardless of directory modified to catch cross-runtime errors --- .github/workflows/go.yml | 4 +- .github/workflows/python.yml | 5 +- go/plugins/dotprompt/picoschema_test.go | 4 +- go/samples/cloud_run_deploy.sh | 16 ++++ go/samples/cloud_run_request.sh | 16 ++++ go/samples/pgvector/pgvector.sql | 16 ++++ js/testapps/next/src/app/globals.css | 18 ++++ js/testapps/next/src/app/layout.tsx | 18 ++++ js/testapps/next/src/app/page.module.css | 18 ++++ js/testapps/next/src/app/page.tsx | 18 ++++ py/.projectile | 0 py/bin/check-licenses | 9 ++ py/bin/cleanup | 10 ++ py/bin/fmt | 11 +++ py/bin/setup | 93 ++++++++++++------- py/captainhook.json | 24 ++++- py/mkdocs.yml | 16 ++++ .../dotprompt/src/dotprompt/__init__.py | 17 ++++ py/packages/genkit/src/genkit/ai/__init__.py | 3 + py/packages/genkit/src/genkit/ai/model.py | 4 +- py/packages/genkit/src/genkit/ai/prompt.py | 5 +- .../genkit/src/genkit/core/__init__.py | 3 + py/packages/genkit/src/genkit/core/action.py | 5 +- .../genkit/src/genkit/core/reflection.py | 5 +- .../genkit/src/genkit/core/registry.py | 5 +- py/packages/genkit/src/genkit/core/tracing.py | 5 +- py/packages/genkit/src/genkit/core/types.py | 4 +- .../genkit/src/genkit/veneer/__init__.py | 5 +- .../src/genkit/plugins/chroma/__init__.py | 3 + .../src/genkit/plugins/firebase/__init__.py | 3 + .../src/genkit/plugins/google_ai/__init__.py | 3 + .../plugins/google_ai/models/__init__.py | 3 + .../genkit/plugins/google_cloud/__init__.py | 3 + .../src/genkit/plugins/ollama/__init__.py | 3 + .../genkit/plugins/ollama/models/__init__.py | 3 + .../src/genkit/plugins/pinecone/__init__.py | 3 + .../src/genkit/plugins/vertex_ai/__init__.py | 3 + .../plugins/vertex_ai/models/__init__.py | 3 + py/samples/hello/hello.py | 5 +- py/tests/smoke/package_test.py | 16 ++++ scripts/release_main.sh | 16 ++++ scripts/release_next.sh | 16 ++++ tests/flow_server_tests.yaml | 16 ++++ tests/reflection_api_tests.yaml | 16 ++++ 44 files changed, 421 insertions(+), 51 deletions(-) delete mode 100644 py/.projectile create mode 100755 py/bin/check-licenses create mode 100755 py/bin/cleanup diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index fe8bccacc..950feea21 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -14,9 +14,7 @@ name: Go tests and checks -on: - pull_request: - paths: 'go/**' +on: pull_request jobs: tests: diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 193606928..7a908ca1c 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -1,9 +1,6 @@ name: Python Checks -on: - pull_request: - paths: - - 'py/**' +on: pull_request jobs: python-checks: diff --git a/go/plugins/dotprompt/picoschema_test.go b/go/plugins/dotprompt/picoschema_test.go index c4cf66937..f25d5f6b1 100644 --- a/go/plugins/dotprompt/picoschema_test.go +++ b/go/plugins/dotprompt/picoschema_test.go @@ -24,13 +24,15 @@ import ( ) // TestPicoschema tests the same cases as picoschema_test.ts. -func TestPicoschema(t *testing.T) { +// Temporarily disabled, see https://github.com/firebase/genkit/pull/1741. +func disableTestPicoschema(t *testing.T) { type test struct { Description string YAML string Want map[string]any } + // TODO(https://github.com/firebase/genkit/issues/1741): This file has been removed #1651 data, err := os.ReadFile(filepath.FromSlash("../../../js/plugins/dotprompt/tests/picoschema_tests.yaml")) if err != nil { t.Fatal(err) diff --git a/go/samples/cloud_run_deploy.sh b/go/samples/cloud_run_deploy.sh index 7f70e097c..8f23623fc 100755 --- a/go/samples/cloud_run_deploy.sh +++ b/go/samples/cloud_run_deploy.sh @@ -1,4 +1,20 @@ #!/bin/sh +# 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 + # This script deploys a sample to Cloud Run. # Run it from this directory (go/samples). diff --git a/go/samples/cloud_run_request.sh b/go/samples/cloud_run_request.sh index f50f856c8..0c7c08c9c 100755 --- a/go/samples/cloud_run_request.sh +++ b/go/samples/cloud_run_request.sh @@ -1,4 +1,20 @@ #!/bin/sh +# 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 + # This script makes a request to a sample application deployed # to cloud run. diff --git a/go/samples/pgvector/pgvector.sql b/go/samples/pgvector/pgvector.sql index a252e9e3d..8e4eec207 100644 --- a/go/samples/pgvector/pgvector.sql +++ b/go/samples/pgvector/pgvector.sql @@ -1,3 +1,19 @@ +-- 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 + -- This SQL enables the vector extension and creates the table and data used -- in the accompanying sample. diff --git a/js/testapps/next/src/app/globals.css b/js/testapps/next/src/app/globals.css index 472befff6..5a90fcc8f 100644 --- a/js/testapps/next/src/app/globals.css +++ b/js/testapps/next/src/app/globals.css @@ -1,3 +1,21 @@ +/** + * 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 + */ + :root { --background: #ffffff; --foreground: #171717; diff --git a/js/testapps/next/src/app/layout.tsx b/js/testapps/next/src/app/layout.tsx index a5c0a8e17..b1e13570d 100644 --- a/js/testapps/next/src/app/layout.tsx +++ b/js/testapps/next/src/app/layout.tsx @@ -1,3 +1,21 @@ +/** + * 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 + */ + import type { Metadata } from 'next'; import { Geist, Geist_Mono } from 'next/font/google'; import './globals.css'; diff --git a/js/testapps/next/src/app/page.module.css b/js/testapps/next/src/app/page.module.css index 62889cafb..c373ff019 100644 --- a/js/testapps/next/src/app/page.module.css +++ b/js/testapps/next/src/app/page.module.css @@ -1,3 +1,21 @@ +/** + * 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 + */ + .title { display: flex; gap: 10px; diff --git a/js/testapps/next/src/app/page.tsx b/js/testapps/next/src/app/page.tsx index 3c78868ca..a43af42b4 100644 --- a/js/testapps/next/src/app/page.tsx +++ b/js/testapps/next/src/app/page.tsx @@ -1,3 +1,21 @@ +/** + * 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 + */ + 'use client'; import { tellJoke } from '@/genkit/joke'; diff --git a/py/.projectile b/py/.projectile deleted file mode 100644 index e69de29bb..000000000 diff --git a/py/bin/check-licenses b/py/bin/check-licenses new file mode 100755 index 000000000..f1d5516d6 --- /dev/null +++ b/py/bin/check-licenses @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -euo pipefail + +TOP_DIR=$(git rev-parse --show-toplevel) + +pushd "${TOP_DIR}/go" +go-licenses check ./... --disallowed_types=restricted,forbidden,reciprocal,unknown +popd diff --git a/py/bin/cleanup b/py/bin/cleanup new file mode 100755 index 000000000..19dfbf248 --- /dev/null +++ b/py/bin/cleanup @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -x +set -euo pipefail + +function genkit::cleanup() { + rm -rf /tmp/e2e* || true && rm -rf /tmp/test-cli* || true +} + +genkit::cleanup diff --git a/py/bin/fmt b/py/bin/fmt index 327cfd5ea..c05600141 100755 --- a/py/bin/fmt +++ b/py/bin/fmt @@ -26,6 +26,17 @@ fi TOP_DIR=$(git rev-parse --show-toplevel) +addlicense \ + -c "Google LLC" \ + -s \ + -ignore '**/.github/**/*' \ + -ignore '**/.mypy_cache/**/*' \ + -ignore '**/bazel-*/**/*' \ + -ignore '**/docs/**/*' \ + -ignore '**/node_modules/**/*' \ + -ignore '**/pnpm-lock.yaml' \ + "$TOP_DIR" + # Format all TOML files. "${TOP_DIR}/py/bin/format_toml_files" if [[ $? -ne 0 ]]; then diff --git a/py/bin/setup b/py/bin/setup index 7cf5efe14..b6168b73c 100755 --- a/py/bin/setup +++ b/py/bin/setup @@ -50,6 +50,12 @@ fi OS_NAME=$(uname) +PYTHON_CLI_TOOLS=( + "httpie" # HTTP client. See: https://httpie.io/ + "mypy" # Static type checker. See: https://mypy.readthedocs.io/en/stable/ + "ruff" # Fast linter. See: https://github.com/astral-sh/ruff +) + # Updates your shell profile to include a path. function genkit::update_path() { local new_path="$1" @@ -77,8 +83,19 @@ function genkit::update_path() { fi } +function genkit::preconfigure_environment() { + git clean -Xfd + genkit::update_path "$HOME/.cargo/bin" + genkit::update_path "$HOME/.local/bin" + genkit::update_path "$HOME/.local/share/pnpm" + genkit::update_path "$HOME/go/bin" + genkit::update_path "$HOME/google-cloud-sdk/bin" +} + # Install all the required tools common to all audiences. function genkit::install_prerequisites() { + genkit::preconfigure_environment + if [[ ${OS_NAME} == "Darwin" && -x "$(command -v brew)" ]]; then # Darwin-based systems. brew install \ @@ -109,20 +126,21 @@ function genkit::install_prerequisites() { # Install rust. curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - ~/.cargo/bin/rustup update + rustup update # Install uv for Python versioning, packaging, and workspace management. curl -LsSf https://astral.sh/uv/install.sh | sh # Install pnpm for JavaScript package management. # See: https://github.com/pnpm/pnpm/issues/6217 - curl -fsSL https://get.pnpm.io/install.sh | env ENV="$HOME/.bashrc" SHELL="$(which bash)" PNPM_VERSION=10.0.0 bash - - - genkit::update_path "$HOME/.cargo/bin" - genkit::update_path "$HOME/.local/share/pnpm" - genkit::update_path "$HOME/go/bin" + curl -fsSL https://get.pnpm.io/install.sh | + env ENV="$HOME/.bashrc" \ + SHELL="$(which bash)" \ + PNPM_VERSION=10.0.0 \ + bash - } +# Install the Google Cloud SDK. function genkit::install_google_cloud_sdk() { # This depends on Python 3.11 and installs it for the user on some systems. if command -v gcloud &>/dev/null; then @@ -135,30 +153,14 @@ function genkit::install_google_cloud_sdk() { gcloud config set disable_usage_reporting true } -# Install all the required tools for CI. -function genkit::install_ci_packages() { - genkit::install_prerequisites - genkit::install_python_cli_tools - genkit::install_go_cli_tools - genkit::install_docs_cli_tools -} - -# Install all the required tools for engineering. -function genkit::install_eng_packages() { - genkit::install_prerequisites - genkit::install_go_cli_tools - genkit::install_cargo_cli_tools - genkit::install_python_cli_tools - genkit::install_docs_cli_tools - genkit::install_google_cloud_sdk - genkit::install_pnpm_cli_tools - genkit::install_pre_commit_hooks -} - # Install all the required tools that have been written in Go. function genkit::install_go_cli_tools() { + go install github.com/Gelio/go-global-update@latest go install github.com/captainhook-go/captainhook/cmd/captainhook@latest + go install github.com/google/addlicense@latest + go install github.com/google/go-licenses@latest go install github.com/jesseduffield/lazygit@latest + go install golang.org/x/vuln/cmd/govulncheck@latest } # Install all the required tools that have been written in Rust. We're assuming @@ -178,12 +180,6 @@ function genkit::install_pnpm_cli_tools() { pnpm add -g genkit-cli } -PYTHON_CLI_TOOLS=( - "httpie" # HTTP client. See: https://httpie.io/ - "mypy" # Static type checker. See: https://mypy.readthedocs.io/en/stable/ - "ruff" # Fast linter. See: https://github.com/astral-sh/ruff -) - # Install all the Python-related formatter and static analysis tools. function genkit::install_python_cli_tools() { for package in "${PYTHON_CLI_TOOLS[@]}"; do @@ -208,7 +204,38 @@ function genkit::install_docs_cli_tools() { # Install pre-commit hooks. function genkit::install_pre_commit_hooks() { - ~/go/bin/captainhook install -f -c ${TOP_DIR}/py/captainhook.json + captainhook install -f -c "${TOP_DIR}/py/captainhook.json" +} + +# Setup genkit. +function genkit::setup_genkit() { + pushd "${TOP_DIR}" + pnpm i + pnpm run setup + popd +} + +# Install all the common packages. +function genkit::install_common_packages() { + genkit::install_prerequisites + genkit::install_go_cli_tools + genkit::install_cargo_cli_tools + genkit::install_python_cli_tools + genkit::install_docs_cli_tools + genkit::install_pnpm_cli_tools +} + +# Install all the required tools for CI. +function genkit::install_ci_packages() { + genkit::install_common_packages +} + +# Install all the required tools for engineering. +function genkit::install_eng_packages() { + genkit::install_common_packages + genkit::install_google_cloud_sdk + genkit::install_pre_commit_hooks + genkit::setup_genkit } # Entry point for the setup script. diff --git a/py/captainhook.json b/py/captainhook.json index 51547e326..b802ec9fe 100644 --- a/py/captainhook.json +++ b/py/captainhook.json @@ -13,7 +13,7 @@ "pre-commit": { "actions": [ { - "run": "py/.hooks/no-commits-on-branches main py" + "run": "py/.hooks/no-commits-on-branches main" }, { "run": "CaptainHook::File.MaxSize", @@ -50,6 +50,17 @@ }, { "run": "go test go/..." + }, + { + "run": "govulncheck -C go ./...", + "conditions": [ + { + "run": "CaptainHook::FileChanged.Any", + "options": { + "files": ["go/go.mod", "go.sum", "*.go"] + } + } + ] } ] }, @@ -88,6 +99,17 @@ { "run": "go test go/..." }, + { + "run": "govulncheck -C go ./...", + "conditions": [ + { + "run": "CaptainHook::FileChanged.Any", + "options": { + "files": ["go/go.mod", "go.sum", "*.go"] + } + } + ] + }, { "run": "py/.hooks/commit-message-format-pre-push" } diff --git a/py/mkdocs.yml b/py/mkdocs.yml index 8945bbc09..f1a5808c3 100644 --- a/py/mkdocs.yml +++ b/py/mkdocs.yml @@ -1,3 +1,19 @@ +# 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 + site_name: Firebase Genkit repo_url: https://github.com/firebase/genkit docs_dir: engdoc diff --git a/py/packages/dotprompt/src/dotprompt/__init__.py b/py/packages/dotprompt/src/dotprompt/__init__.py index 6454f134c..f1ab80247 100644 --- a/py/packages/dotprompt/src/dotprompt/__init__.py +++ b/py/packages/dotprompt/src/dotprompt/__init__.py @@ -1,2 +1,19 @@ +# 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 + + def hello() -> str: return 'Hello from dotprompt!' diff --git a/py/packages/genkit/src/genkit/ai/__init__.py b/py/packages/genkit/src/genkit/ai/__init__.py index cfa00dc6e..3ec7b0ed9 100644 --- a/py/packages/genkit/src/genkit/ai/__init__.py +++ b/py/packages/genkit/src/genkit/ai/__init__.py @@ -11,6 +11,9 @@ # 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 + """ AI Foundations for Genkit diff --git a/py/packages/genkit/src/genkit/ai/model.py b/py/packages/genkit/src/genkit/ai/model.py index 250056fff..d0a0a9234 100644 --- a/py/packages/genkit/src/genkit/ai/model.py +++ b/py/packages/genkit/src/genkit/ai/model.py @@ -1,4 +1,4 @@ -# Copyright 2025 Google Inc. +# 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. @@ -11,6 +11,8 @@ # 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 from typing import Callable from genkit.core.types import GenerateRequest, GenerateResponse diff --git a/py/packages/genkit/src/genkit/ai/prompt.py b/py/packages/genkit/src/genkit/ai/prompt.py index 71fa6c00c..a32e10939 100644 --- a/py/packages/genkit/src/genkit/ai/prompt.py +++ b/py/packages/genkit/src/genkit/ai/prompt.py @@ -1,4 +1,4 @@ -# Copyright 2025 Google Inc. +# 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. @@ -11,6 +11,9 @@ # 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 + from genkit.core.types import GenerateRequest from typing import Callable, Optional, Any diff --git a/py/packages/genkit/src/genkit/core/__init__.py b/py/packages/genkit/src/genkit/core/__init__.py index 7d51106d6..6fe59e083 100644 --- a/py/packages/genkit/src/genkit/core/__init__.py +++ b/py/packages/genkit/src/genkit/core/__init__.py @@ -11,6 +11,9 @@ # 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 + """ Core Foundations for Genkit diff --git a/py/packages/genkit/src/genkit/core/action.py b/py/packages/genkit/src/genkit/core/action.py index e3cb8d1eb..e11a5d996 100644 --- a/py/packages/genkit/src/genkit/core/action.py +++ b/py/packages/genkit/src/genkit/core/action.py @@ -1,4 +1,4 @@ -# Copyright 2025 Google Inc. +# 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. @@ -11,6 +11,9 @@ # 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 + import inspect import json diff --git a/py/packages/genkit/src/genkit/core/reflection.py b/py/packages/genkit/src/genkit/core/reflection.py index f638c5e11..b1366d1e6 100644 --- a/py/packages/genkit/src/genkit/core/reflection.py +++ b/py/packages/genkit/src/genkit/core/reflection.py @@ -1,4 +1,4 @@ -# Copyright 2025 Google Inc. +# 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. @@ -11,6 +11,9 @@ # 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 + import json diff --git a/py/packages/genkit/src/genkit/core/registry.py b/py/packages/genkit/src/genkit/core/registry.py index c28348fdb..ac69e723c 100644 --- a/py/packages/genkit/src/genkit/core/registry.py +++ b/py/packages/genkit/src/genkit/core/registry.py @@ -1,4 +1,4 @@ -# Copyright 2025 Google Inc. +# 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. @@ -11,6 +11,9 @@ # 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 + from genkit.core.action import Action from typing import Dict diff --git a/py/packages/genkit/src/genkit/core/tracing.py b/py/packages/genkit/src/genkit/core/tracing.py index 4b03e8447..8fca4d312 100644 --- a/py/packages/genkit/src/genkit/core/tracing.py +++ b/py/packages/genkit/src/genkit/core/tracing.py @@ -1,4 +1,4 @@ -# Copyright 2025 Google Inc. +# 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. @@ -11,6 +11,9 @@ # 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 + import json import os diff --git a/py/packages/genkit/src/genkit/core/types.py b/py/packages/genkit/src/genkit/core/types.py index 293ddb7b1..f7fc0dd96 100644 --- a/py/packages/genkit/src/genkit/core/types.py +++ b/py/packages/genkit/src/genkit/core/types.py @@ -1,4 +1,4 @@ -# Copyright 2022 Google Inc. +# 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. @@ -11,6 +11,8 @@ # 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 # generated by datamodel-codegen: diff --git a/py/packages/genkit/src/genkit/veneer/__init__.py b/py/packages/genkit/src/genkit/veneer/__init__.py index 20224e957..22cdc86fc 100644 --- a/py/packages/genkit/src/genkit/veneer/__init__.py +++ b/py/packages/genkit/src/genkit/veneer/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2025 Google Inc. +# 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. @@ -11,6 +11,9 @@ # 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 + import atexit import datetime diff --git a/py/plugins/chroma/src/genkit/plugins/chroma/__init__.py b/py/plugins/chroma/src/genkit/plugins/chroma/__init__.py index b1bde32b9..6678a0a4a 100644 --- a/py/plugins/chroma/src/genkit/plugins/chroma/__init__.py +++ b/py/plugins/chroma/src/genkit/plugins/chroma/__init__.py @@ -11,6 +11,9 @@ # 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 + """ Chroma Plugin for Genkit. diff --git a/py/plugins/firebase/src/genkit/plugins/firebase/__init__.py b/py/plugins/firebase/src/genkit/plugins/firebase/__init__.py index 74417b773..99e7e023a 100644 --- a/py/plugins/firebase/src/genkit/plugins/firebase/__init__.py +++ b/py/plugins/firebase/src/genkit/plugins/firebase/__init__.py @@ -11,6 +11,9 @@ # 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 + """ Firebase Plugin for Genkit. diff --git a/py/plugins/google-ai/src/genkit/plugins/google_ai/__init__.py b/py/plugins/google-ai/src/genkit/plugins/google_ai/__init__.py index 801954c47..a7ddbf40d 100644 --- a/py/plugins/google-ai/src/genkit/plugins/google_ai/__init__.py +++ b/py/plugins/google-ai/src/genkit/plugins/google_ai/__init__.py @@ -11,6 +11,9 @@ # 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 + """ Google AI Plugin for Genkit diff --git a/py/plugins/google-ai/src/genkit/plugins/google_ai/models/__init__.py b/py/plugins/google-ai/src/genkit/plugins/google_ai/models/__init__.py index 1a3a0d5db..b9528eeb8 100644 --- a/py/plugins/google-ai/src/genkit/plugins/google_ai/models/__init__.py +++ b/py/plugins/google-ai/src/genkit/plugins/google_ai/models/__init__.py @@ -11,6 +11,9 @@ # 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 + """ Google AI Models for Genkit. diff --git a/py/plugins/google-cloud/src/genkit/plugins/google_cloud/__init__.py b/py/plugins/google-cloud/src/genkit/plugins/google_cloud/__init__.py index 9ebf26ecf..cdf27d88f 100644 --- a/py/plugins/google-cloud/src/genkit/plugins/google_cloud/__init__.py +++ b/py/plugins/google-cloud/src/genkit/plugins/google_cloud/__init__.py @@ -11,6 +11,9 @@ # 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 + """ Google Cloud Plugin for Genkit. diff --git a/py/plugins/ollama/src/genkit/plugins/ollama/__init__.py b/py/plugins/ollama/src/genkit/plugins/ollama/__init__.py index ce95d9085..ac9f8e05a 100644 --- a/py/plugins/ollama/src/genkit/plugins/ollama/__init__.py +++ b/py/plugins/ollama/src/genkit/plugins/ollama/__init__.py @@ -11,6 +11,9 @@ # 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 + """ Ollama Plugin for Genkit. diff --git a/py/plugins/ollama/src/genkit/plugins/ollama/models/__init__.py b/py/plugins/ollama/src/genkit/plugins/ollama/models/__init__.py index 4c44cab18..f06fbcebe 100644 --- a/py/plugins/ollama/src/genkit/plugins/ollama/models/__init__.py +++ b/py/plugins/ollama/src/genkit/plugins/ollama/models/__init__.py @@ -11,6 +11,9 @@ # 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 + """ Ollama Models for Genkit. diff --git a/py/plugins/pinecone/src/genkit/plugins/pinecone/__init__.py b/py/plugins/pinecone/src/genkit/plugins/pinecone/__init__.py index f93dbe3d4..c35b7e1ac 100644 --- a/py/plugins/pinecone/src/genkit/plugins/pinecone/__init__.py +++ b/py/plugins/pinecone/src/genkit/plugins/pinecone/__init__.py @@ -11,6 +11,9 @@ # 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 + """ Pinecone Plugin for Genkit. diff --git a/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/__init__.py b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/__init__.py index a59cc1c58..307d8dfda 100644 --- a/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/__init__.py +++ b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/__init__.py @@ -11,6 +11,9 @@ # 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 + """ Google Cloud Vertex AI Plugin for Genkit. diff --git a/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/models/__init__.py b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/models/__init__.py index fdd7ebeba..40ca43c53 100644 --- a/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/models/__init__.py +++ b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/models/__init__.py @@ -11,6 +11,9 @@ # 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 + """ Google Cloud Vertex AI Models for Genkit. diff --git a/py/samples/hello/hello.py b/py/samples/hello/hello.py index 1a1375a22..cf379fa7f 100644 --- a/py/samples/hello/hello.py +++ b/py/samples/hello/hello.py @@ -1,4 +1,4 @@ -# Copyright 2025 Google Inc. +# 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. @@ -11,6 +11,9 @@ # 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 + from genkit.core.types import Message, TextPart, GenerateRequest from genkit.plugins.vertex_ai import vertexAI, gemini diff --git a/py/tests/smoke/package_test.py b/py/tests/smoke/package_test.py index 9f72ec5cc..8fb0983e1 100644 --- a/py/tests/smoke/package_test.py +++ b/py/tests/smoke/package_test.py @@ -1,3 +1,19 @@ +# 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 + """Smoke tests for package structure.""" # TODO: Replace this with proper imports once we have a proper implementation. diff --git a/scripts/release_main.sh b/scripts/release_main.sh index 9d41b080d..8e57662e8 100755 --- a/scripts/release_main.sh +++ b/scripts/release_main.sh @@ -1,4 +1,20 @@ #!/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 + # git clone git@github.com:firebase/genkit.git # cd genkit diff --git a/scripts/release_next.sh b/scripts/release_next.sh index ce49e0026..52a8adafd 100755 --- a/scripts/release_next.sh +++ b/scripts/release_next.sh @@ -1,4 +1,20 @@ #!/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 + # git clone git@github.com:firebase/genkit.git # cd genkit diff --git a/tests/flow_server_tests.yaml b/tests/flow_server_tests.yaml index 529deb9c2..043b16423 100644 --- a/tests/flow_server_tests.yaml +++ b/tests/flow_server_tests.yaml @@ -1,3 +1,19 @@ +# 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 + # This file describes the responses to HTTP requests made # to the flow server diff --git a/tests/reflection_api_tests.yaml b/tests/reflection_api_tests.yaml index 9ef520dc2..1afcab510 100644 --- a/tests/reflection_api_tests.yaml +++ b/tests/reflection_api_tests.yaml @@ -1,3 +1,19 @@ +# 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 + # This file describes the responses to HTTP requests made # to the reflection API. From c68d27f64b8bde069531404d947c44bdb446bbaf Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Fri, 31 Jan 2025 17:32:58 -0500 Subject: [PATCH 462/562] chore(js): moved defineFormat and defineInterrupt to beta, api ref fixes (#1742) --- js/core/src/context.ts | 3 ++ js/core/src/index.ts | 1 + js/genkit/src/beta.ts | 67 +++++++++++++++++++++++++++++++- js/genkit/src/genkit.ts | 66 +------------------------------ js/genkit/src/index.ts | 1 + js/genkit/tests/formats_test.ts | 4 +- js/genkit/tests/generate_test.ts | 10 ++--- js/genkit/typedoc.json | 17 ++++---- 8 files changed, 88 insertions(+), 81 deletions(-) diff --git a/js/core/src/context.ts b/js/core/src/context.ts index bc68b3f2e..87b3de09e 100644 --- a/js/core/src/context.ts +++ b/js/core/src/context.ts @@ -19,6 +19,9 @@ import { HasRegistry, Registry } from './registry.js'; const contextAlsKey = 'core.auth.context'; +/** + * Action side channel data, like auth and other invocation context infromation provided by the invoker. + */ export interface ActionContext { /** Information about the currently authenticated user if provided. */ auth?: Record; diff --git a/js/core/src/index.ts b/js/core/src/index.ts index 1039a0cc5..3c3ea2e8e 100644 --- a/js/core/src/index.ts +++ b/js/core/src/index.ts @@ -37,6 +37,7 @@ export { type Flow, type FlowConfig, type FlowFn, + type FlowSideChannel, } from './flow.js'; export * from './plugin.js'; export * from './reflection.js'; diff --git a/js/genkit/src/beta.ts b/js/genkit/src/beta.ts index 266c8033b..f36c21e65 100644 --- a/js/genkit/src/beta.ts +++ b/js/genkit/src/beta.ts @@ -14,16 +14,25 @@ * limitations under the License. */ -import { ExecutablePrompt, isExecutablePrompt } from '@genkit-ai/ai'; +import { + defineInterrupt, + ExecutablePrompt, + InterruptConfig, + isExecutablePrompt, + ToolAction, +} from '@genkit-ai/ai'; import { Chat, ChatOptions } from '@genkit-ai/ai/chat'; +import { defineFormat } from '@genkit-ai/ai/formats'; import { + getCurrentSession, Session, SessionData, SessionError, SessionOptions, - getCurrentSession, } from '@genkit-ai/ai/session'; +import { z } from '@genkit-ai/core'; import { v4 as uuidv4 } from 'uuid'; +import { Formatter } from './formats'; import { Genkit, GenkitOptions } from './genkit'; /** @@ -148,4 +157,58 @@ export class GenkitBeta extends Genkit { } return currentSession as Session; } + + /** + * Defines and registers a custom model output formatter. + * + * Here's an example of a custom JSON output formatter: + * + * ```ts + * import { extractJson } from 'genkit/extract'; + * + * ai.defineFormat( + * { name: 'customJson' }, + * (schema) => { + * let instructions: string | undefined; + * if (schema) { + * instructions = `Output should be in JSON format and conform to the following schema: + * \`\`\` + * ${JSON.stringify(schema)} + * \`\`\` + * `; + * } + * return { + * parseChunk: (chunk) => extractJson(chunk.accumulatedText), + * parseMessage: (message) => extractJson(message.text), + * instructions, + * }; + * } + * ); + * + * const { output } = await ai.generate({ + * prompt: 'Invent a menu item for a pirate themed restaurant.', + * output: { format: 'customJson', schema: MenuItemSchema }, + * }); + * ``` + */ + defineFormat( + options: { + name: string; + } & Formatter['config'], + handler: Formatter['handler'] + ): { config: Formatter['config']; handler: Formatter['handler'] } { + return defineFormat(this.registry, options, handler); + } + + /** + * Defines and registers an interrupt. + * + * Interrupts are special tools that halt model processing and return control back to the caller. Interrupts make it simpler to implement + * "human-in-the-loop" and out-of-band processing patterns that require waiting on external actions to complete. + */ + defineInterrupt( + config: InterruptConfig + ): ToolAction { + return defineInterrupt(this.registry, config); + } } diff --git a/js/genkit/src/genkit.ts b/js/genkit/src/genkit.ts index 7d7f87648..8795cc6bb 100644 --- a/js/genkit/src/genkit.ts +++ b/js/genkit/src/genkit.ts @@ -31,7 +31,6 @@ import { GenerateStreamResponse, GenerationCommonConfigSchema, IndexerParams, - InterruptConfig, ModelArgument, Part, PromptConfig, @@ -44,7 +43,6 @@ import { ToolAction, ToolConfig, defineHelper, - defineInterrupt, definePartial, definePrompt, defineTool, @@ -70,11 +68,7 @@ import { EvaluatorFn, defineEvaluator, } from '@genkit-ai/ai/evaluator'; -import { - Formatter, - configureFormats, - defineFormat, -} from '@genkit-ai/ai/formats'; +import { configureFormats } from '@genkit-ai/ai/formats'; import { DefineModelOptions, GenerateResponseChunkData, @@ -154,8 +148,6 @@ export interface GenkitOptions { export class Genkit implements HasRegistry { /** Developer-configured options. */ readonly options: GenkitOptions; - /** Environments that have been configured (at minimum dev). */ - readonly configuredEnvs = new Set(['dev']); /** Registry instance that is exclusively modified by this Genkit instance. */ readonly registry: Registry; /** Reflection server for this registry. May be null if not started. */ @@ -169,7 +161,7 @@ export class Genkit implements HasRegistry { this.configure(); if (isDevEnv() && !disableReflectionApi) { this.reflectionServer = new ReflectionServer(this.registry, { - configuredEnvs: [...this.configuredEnvs], + configuredEnvs: ['dev'], }); this.reflectionServer.start().catch((e) => logger.error); } @@ -205,18 +197,6 @@ export class Genkit implements HasRegistry { return defineTool(this.registry, config, fn); } - /** - * Defines and registers an interrupt. - * - * Interrupts are special tools that halt model processing and return control back to the caller. Interrupts make it simpler to implement - * "human-in-the-loop" and out-of-band processing patterns that require waiting on external actions to complete. - */ - defineInterrupt( - config: InterruptConfig - ): ToolAction { - return defineInterrupt(this.registry, config); - } - /** * Defines and registers a schema from a Zod schema. * @@ -226,48 +206,6 @@ export class Genkit implements HasRegistry { return defineSchema(this.registry, name, schema); } - /** - * Defines and registers a custom model output formatter. - * - * Here's an example of a custom JSON output formatter: - * - * ```ts - * import { extractJson } from 'genkit/extract'; - * - * ai.defineFormat( - * { name: 'customJson' }, - * (schema) => { - * let instructions: string | undefined; - * if (schema) { - * instructions = `Output should be in JSON format and conform to the following schema: - * \`\`\` - * ${JSON.stringify(schema)} - * \`\`\` - * `; - * } - * return { - * parseChunk: (chunk) => extractJson(chunk.accumulatedText), - * parseMessage: (message) => extractJson(message.text), - * instructions, - * }; - * } - * ); - * - * const { output } = await ai.generate({ - * prompt: 'Invent a menu item for a pirate themed restaurant.', - * output: { format: 'customJson', schema: MenuItemSchema }, - * }); - * ``` - */ - defineFormat( - options: { - name: string; - } & Formatter['config'], - handler: Formatter['handler'] - ): { config: Formatter['config']; handler: Formatter['handler'] } { - return defineFormat(this.registry, options, handler); - } - /** * Defines and registers a schema from a JSON schema. * diff --git a/js/genkit/src/index.ts b/js/genkit/src/index.ts index 938711d4d..14e216877 100644 --- a/js/genkit/src/index.ts +++ b/js/genkit/src/index.ts @@ -133,6 +133,7 @@ export { type Flow, type FlowConfig, type FlowFn, + type FlowSideChannel, type JSONSchema, type JSONSchema7, type Middleware, diff --git a/js/genkit/tests/formats_test.ts b/js/genkit/tests/formats_test.ts index 8613b1553..29e22ccf4 100644 --- a/js/genkit/tests/formats_test.ts +++ b/js/genkit/tests/formats_test.ts @@ -16,11 +16,11 @@ import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; -import { Genkit, genkit } from '../src/genkit'; +import { GenkitBeta, genkit } from '../src/beta'; import { defineEchoModel } from './helpers'; describe('formats', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({}); diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index 4fe0278d3..308241b4b 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -19,7 +19,7 @@ import { z } from '@genkit-ai/core'; import * as assert from 'assert'; import { beforeEach, describe, it } from 'node:test'; import { modelRef } from '../../ai/src/model'; -import { Genkit, genkit } from '../src/genkit'; +import { GenkitBeta, genkit } from '../src/beta'; import { ProgrammableModel, defineEchoModel, @@ -29,7 +29,7 @@ import { describe('generate', () => { describe('default model', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({ @@ -145,7 +145,7 @@ describe('generate', () => { }); describe('streaming', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({}); @@ -253,7 +253,7 @@ describe('generate', () => { }); describe('config', () => { - let ai: Genkit; + let ai: GenkitBeta; beforeEach(() => { ai = genkit({}); @@ -303,7 +303,7 @@ describe('generate', () => { }); describe('tools', () => { - let ai: Genkit; + let ai: GenkitBeta; let pm: ProgrammableModel; beforeEach(() => { diff --git a/js/genkit/typedoc.json b/js/genkit/typedoc.json index 0405ec0db..c44a81505 100644 --- a/js/genkit/typedoc.json +++ b/js/genkit/typedoc.json @@ -1,20 +1,21 @@ { "entryPoints": [ "src/index.ts", + "src/beta.ts", + "src/client/index.ts", + "src/model.ts", + "src/tool.ts", + "src/retriever.ts", + "src/reranker.ts", + "src/plugin.ts", + "src/middleware.ts", "src/registry.ts", "src/tracing.ts", "src/logging.ts", "src/schema.ts", - "src/retriever.ts", - "src/reranker.ts", "src/embedder.ts", "src/evaluator.ts", - "src/model.ts", - "src/middleware.ts", "src/extract.ts", - "src/testing.ts", - "src/tool.ts", - "src/plugin.ts", - "src/client/index.ts" + "src/testing.ts" ] } From 485ac994b0b9fa171e1a426ba90df5869a681e0a Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Fri, 31 Jan 2025 17:51:26 -0800 Subject: [PATCH 463/562] chore(js/ai): interrupts: rename 'reply' to 'respond' and 'replySchema' to 'outputSchema' (#1745) --- js/ai/src/generate.ts | 13 ++++----- js/ai/src/generate/resolve-tool-requests.ts | 10 +++---- js/ai/src/model.ts | 2 +- js/ai/src/tool.ts | 29 ++++++--------------- js/ai/tests/generate/generate_test.ts | 8 +++--- js/ai/tests/tool_test.ts | 24 ++++++++--------- js/genkit/tests/generate_test.ts | 4 +-- 7 files changed, 39 insertions(+), 51 deletions(-) diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index 13da1c66c..825b5d5ef 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -67,15 +67,15 @@ export interface OutputOptions { /** ResumeOptions configure how to resume generation after an interrupt. */ export interface ResumeOptions { /** - * reply should contain a single or list of `toolResponse` parts corresponding + * respond should contain a single or list of `toolResponse` parts corresponding * to interrupt `toolRequest` parts from the most recent model message. Each * entry must have a matching `name` and `ref` (if supplied) for its `toolRequest` * counterpart. * - * Tools have a `.reply` helper method to construct a reply ToolResponse and validate - * the data against its schema. Call `myTool.reply(interruptToolRequest, yourReplyData)`. + * Tools have a `.respond` helper method to construct a reply ToolResponse and validate + * the data against its schema. Call `myTool.respond(interruptToolRequest, yourReplyData)`. */ - reply?: ToolResponsePart | ToolResponsePart[]; + respond?: ToolResponsePart | ToolResponsePart[]; /** * restart will run a tool again with additionally supplied metadata passed through as * a `resumed` option in the second argument. This allows for scenarios like conditionally @@ -83,6 +83,7 @@ export interface ResumeOptions { * * Tools have a `.restart` helper method to construct a restart ToolRequest. Call * `myTool.restart(interruptToolRequest, resumeMetadata)`. + * */ restart?: ToolRequestPart | ToolRequestPart[]; /** Additional metadata to annotate the created tool message with in the "resume" key. */ @@ -127,7 +128,7 @@ export interface GenerateOptions< * * const resumedResponse = await ai.generate({ * messages: response.messages, - * resume: myInterrupt.reply(interrupt, {note: "this is the reply data"}), + * resume: myInterrupt.respond(interrupt, {note: "this is the reply data"}), * }); * ``` */ @@ -361,7 +362,7 @@ export async function generate< }, // coerce reply and restart into arrays for the action schema resume: resolvedOptions.resume && { - reply: [resolvedOptions.resume.reply || []].flat(), + respond: [resolvedOptions.resume.respond || []].flat(), restart: [resolvedOptions.resume.restart || []].flat(), metadata: resolvedOptions.resume.metadata, }, diff --git a/js/ai/src/generate/resolve-tool-requests.ts b/js/ai/src/generate/resolve-tool-requests.ts index 220350f70..f87c6495b 100644 --- a/js/ai/src/generate/resolve-tool-requests.ts +++ b/js/ai/src/generate/resolve-tool-requests.ts @@ -267,12 +267,12 @@ async function resolveResumedToolRequest( } // if there's a corresponding reply, append it to toolResponses - const replyResponse = findCorrespondingToolResponse( - rawRequest.resume?.reply || [], + const providedResponse = findCorrespondingToolResponse( + rawRequest.resume?.respond || [], part ); - if (replyResponse) { - const toolResponse = replyResponse; + if (providedResponse) { + const toolResponse = providedResponse; // remove the 'interrupt' but leave a 'resolvedInterrupt' const { interrupt, ...metadata } = part.metadata || {}; @@ -324,7 +324,7 @@ async function resolveResumedToolRequest( throw new GenkitError({ status: 'INVALID_ARGUMENT', - message: `Unresolved tool request '${part.toolRequest.name}${part.toolRequest.ref ? `#${part.toolRequest.ref}` : ''} was not handled by the 'resume' argument. You must supply replies or restarts for all interrupted tool requests.'`, + message: `Unresolved tool request '${part.toolRequest.name}${part.toolRequest.ref ? `#${part.toolRequest.ref}` : ''}' was not handled by the 'resume' argument. You must supply replies or restarts for all interrupted tool requests.'`, }); } diff --git a/js/ai/src/model.ts b/js/ai/src/model.ts index b2a8884c0..c9ade16a5 100644 --- a/js/ai/src/model.ts +++ b/js/ai/src/model.ts @@ -714,7 +714,7 @@ export const GenerateActionOptionsSchema = z.object({ /** Options for resuming an interrupted generation. */ resume: z .object({ - reply: z.array(ToolResponsePartSchema).optional(), + respond: z.array(ToolResponsePartSchema).optional(), restart: z.array(ToolRequestPartSchema).optional(), metadata: z.record(z.any()).optional(), }) diff --git a/js/ai/src/tool.ts b/js/ai/src/tool.ts index 722a62590..e618c9bda 100644 --- a/js/ai/src/tool.ts +++ b/js/ai/src/tool.ts @@ -47,20 +47,21 @@ export type ToolAction< }; }; /** - * reply constructs a tool response corresponding to the provided interrupt tool request + * respond constructs a tool response corresponding to the provided interrupt tool request * using the provided reply data, validating it against the output schema of the tool if * it exists. */ - reply( + respond( /** The interrupt tool request to which you want to respond. */ interrupt: ToolRequestPart, /** * The data with which you want to respond. Must conform to a tool's output schema or an * interrupt's input schema. **/ - replyData: z.infer, + outputData: z.infer, options?: { metadata?: Record } ): ToolResponsePart; + /** * restart constructs a tool request corresponding to the provided interrupt tool request * that will then re-trigger the tool after e.g. a user confirms. The `resumedMetadata` @@ -263,7 +264,7 @@ export function defineTool( }); } ); - (a as ToolAction).reply = (interrupt, replyData, options) => { + (a as ToolAction).respond = (interrupt, replyData, options) => { parseSchema(replyData, { jsonSchema: config.outputJsonSchema, schema: config.outputSchema, @@ -275,7 +276,7 @@ export function defineTool( output: replyData, }), metadata: { - reply: options?.metadata || true, + interruptResponse: options?.metadata || true, }, }; }; @@ -309,11 +310,7 @@ export function defineTool( export type InterruptConfig< I extends z.ZodTypeAny = z.ZodTypeAny, R extends z.ZodTypeAny = z.ZodTypeAny, -> = Omit, 'outputSchema' | 'outputJsonSchema'> & { - /** replySchema defines the Zod schema that this interrupt needs when resuming */ - replySchema?: R; - /** replyJsonSchema defines the JSON schema that this interrupt needs when resuming */ - replyJsonSchema?: JSONSchema7; +> = ToolConfig & { /** requestMetadata adds additional `interrupt` metadata to the `toolRequest` generated by the interrupt */ requestMetadata?: | Record @@ -334,17 +331,7 @@ export function defineInterrupt( registry: Registry, config: InterruptConfig ): ToolAction { - const { - replySchema: outputSchema, - replyJsonSchema: outputJsonSchema, - requestMetadata, - ...overlap - } = config; - const toolConfig: ToolConfig = { - ...overlap, - outputSchema, - outputJsonSchema, - }; + const { requestMetadata, ...toolConfig } = config; return defineTool( registry, diff --git a/js/ai/tests/generate/generate_test.ts b/js/ai/tests/generate/generate_test.ts index 085e69070..0a2ea2e5c 100644 --- a/js/ai/tests/generate/generate_test.ts +++ b/js/ai/tests/generate/generate_test.ts @@ -256,25 +256,25 @@ describe('toGenerateRequest', () => { }, { should: - 'throw a PRECONDITION_FAILED error if trying to resume without a model message', + 'throw a FAILED_PRECONDITION error if trying to resume without a model message', prompt: { messages: [{ role: 'system', content: [{ text: 'sys' }] }], resume: { - reply: { toolResponse: { name: 'test', output: { foo: 'bar' } } }, + respond: { toolResponse: { name: 'test', output: { foo: 'bar' } } }, }, }, throws: 'FAILED_PRECONDITION', }, { should: - 'throw a PRECONDITION_FAILED error if trying to resume a model message without toolRequests', + 'throw a FAILED_PRECONDITION error if trying to resume a model message without toolRequests', prompt: { messages: [ { role: 'user', content: [{ text: 'hi' }] }, { role: 'model', content: [{ text: 'there' }] }, ], resume: { - reply: { toolResponse: { name: 'test', output: { foo: 'bar' } } }, + respond: { toolResponse: { name: 'test', output: { foo: 'bar' } } }, }, }, throws: 'FAILED_PRECONDITION', diff --git a/js/ai/tests/tool_test.ts b/js/ai/tests/tool_test.ts index 3d184a517..5da178792 100644 --- a/js/ai/tests/tool_test.ts +++ b/js/ai/tests/tool_test.ts @@ -86,17 +86,17 @@ describe('defineInterrupt', () => { }); it('should register the reply schema / json schema as the output schema of the tool', () => { - const ReplySchema = z.object({ foo: z.string() }); + const ResponseSchema = z.object({ foo: z.string() }); const simple = defineInterrupt(registry, { name: 'simple', description: 'simple', - replySchema: ReplySchema, + outputSchema: ResponseSchema, }); - assert.equal(simple.__action.outputSchema, ReplySchema); + assert.equal(simple.__action.outputSchema, ResponseSchema); const simple2 = defineInterrupt(registry, { name: 'simple2', description: 'simple2', - replyJsonSchema: { type: 'string' }, + outputJsonSchema: { type: 'string' }, }); assert.deepStrictEqual(simple2.__action.outputJsonSchema, { type: 'string', @@ -110,7 +110,7 @@ describe('defineTool', () => { registry = new Registry(); }); - describe('.reply()', () => { + describe('.respond()', () => { it('constructs a ToolResponsePart', () => { const t = defineTool( registry, @@ -118,14 +118,14 @@ describe('defineTool', () => { async () => {} ); assert.deepStrictEqual( - t.reply({ toolRequest: { name: 'test', input: {} } }, 'output'), + t.respond({ toolRequest: { name: 'test', input: {} } }, 'output'), { toolResponse: { name: 'test', output: 'output', }, metadata: { - reply: true, + interruptResponse: true, }, } ); @@ -138,7 +138,7 @@ describe('defineTool', () => { async () => {} ); assert.deepStrictEqual( - t.reply({ toolRequest: { name: 'test', input: {} } }, 'output', { + t.respond({ toolRequest: { name: 'test', input: {} } }, 'output', { metadata: { extra: 'data' }, }), { @@ -147,7 +147,7 @@ describe('defineTool', () => { output: 'output', }, metadata: { - reply: { extra: 'data' }, + interruptResponse: { extra: 'data' }, }, } ); @@ -161,7 +161,7 @@ describe('defineTool', () => { ); assert.throws( () => { - t.reply( + t.respond( { toolRequest: { name: 'test', input: {} } }, 'not_a_number' as any ); @@ -170,14 +170,14 @@ describe('defineTool', () => { ); assert.deepStrictEqual( - t.reply({ toolRequest: { name: 'test', input: {} } }, 55), + t.respond({ toolRequest: { name: 'test', input: {} } }, 55), { toolResponse: { name: 'test', output: 55, }, metadata: { - reply: true, + interruptResponse: true, }, } ); diff --git a/js/genkit/tests/generate_test.ts b/js/genkit/tests/generate_test.ts index 308241b4b..05b1826f9 100644 --- a/js/genkit/tests/generate_test.ts +++ b/js/genkit/tests/generate_test.ts @@ -872,7 +872,7 @@ describe('generate', () => { messages, tools: [interrupter, resumable, truth], resume: { - reply: interrupter.reply( + respond: interrupter.respond( { toolRequest: { name: 'interrupter', input: {} }, metadata: { interrupt: true }, @@ -928,7 +928,7 @@ describe('generate', () => { [ { metadata: { - reply: true, + interruptResponse: true, }, toolResponse: { name: 'interrupter', From 9a8861721eebb641006a039f4a202869c7339e87 Mon Sep 17 00:00:00 2001 From: Yesudeep Mangalapilly Date: Fri, 31 Jan 2025 17:55:45 -0800 Subject: [PATCH 464/562] feat: add stub project for handlebarz implementation #1710 (#1719) Issue: https://github.com/firebase/genkit/issues/1710 Changelog: - [ ] Adds stub project to py/packages for handlebarz python implementation - [ ] Updates workspace configuration and lock file - [ ] Updates build dist configuration so that pre-commits check handlebarz --- py/bin/build_dists | 1 + py/packages/dotprompt/pyproject.toml | 4 +- py/packages/genkit/pyproject.toml | 1 + py/packages/handlebarz/LICENSE | 201 ++++++++++++++++++ py/packages/handlebarz/README.md | 1 + py/packages/handlebarz/pyproject.toml | 33 +++ .../handlebarz/src/handlebarz/__init__.py | 2 + .../handlebarz/src/handlebarz/py.typed | 0 py/pyproject.toml | 2 + py/uv.lock | 16 ++ 10 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 py/packages/handlebarz/LICENSE create mode 100644 py/packages/handlebarz/README.md create mode 100644 py/packages/handlebarz/pyproject.toml create mode 100644 py/packages/handlebarz/src/handlebarz/__init__.py create mode 100644 py/packages/handlebarz/src/handlebarz/py.typed diff --git a/py/bin/build_dists b/py/bin/build_dists index 3eb18635c..f0a3c2274 100755 --- a/py/bin/build_dists +++ b/py/bin/build_dists @@ -28,6 +28,7 @@ TOP_DIR=$(git rev-parse --show-toplevel) PROJECT_DIRS=( "packages/dotprompt" "packages/genkit" + "packages/handlebarz" "plugins/chroma" "plugins/firebase" "plugins/google-ai" diff --git a/py/packages/dotprompt/pyproject.toml b/py/packages/dotprompt/pyproject.toml index bc344a7c8..f469a201d 100644 --- a/py/packages/dotprompt/pyproject.toml +++ b/py/packages/dotprompt/pyproject.toml @@ -14,7 +14,9 @@ classifiers = [ "Topic :: Scientific/Engineering :: Artificial Intelligence", "Topic :: Software Development :: Libraries", ] -dependencies = [] +dependencies = [ + "handlebarz", +] description = "Dotprompt is a language-neutral executable prompt template file format for Generative AI." license = { text = "Apache-2.0" } name = "dotprompt" diff --git a/py/packages/genkit/pyproject.toml b/py/packages/genkit/pyproject.toml index a41029702..a37e37ada 100644 --- a/py/packages/genkit/pyproject.toml +++ b/py/packages/genkit/pyproject.toml @@ -19,6 +19,7 @@ dependencies = [ "opentelemetry-sdk>=1.29.0", "pydantic>=2.10.5", "requests>=2.32.3", + "dotprompt", ] description = "Genkit AI Framework" license = { text = "Apache-2.0" } diff --git a/py/packages/handlebarz/LICENSE b/py/packages/handlebarz/LICENSE new file mode 100644 index 000000000..220539673 --- /dev/null +++ b/py/packages/handlebarz/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. diff --git a/py/packages/handlebarz/README.md b/py/packages/handlebarz/README.md new file mode 100644 index 000000000..78f9d6439 --- /dev/null +++ b/py/packages/handlebarz/README.md @@ -0,0 +1 @@ +# Handlebarz diff --git a/py/packages/handlebarz/pyproject.toml b/py/packages/handlebarz/pyproject.toml new file mode 100644 index 000000000..6196aa84f --- /dev/null +++ b/py/packages/handlebarz/pyproject.toml @@ -0,0 +1,33 @@ +[project] +authors = [{ name = "Google" }] +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries", + "Topic :: Software Development :: Code Generators", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Text Processing :: Markup", +] +dependencies = [] +description = "Python implementation for Handlebars.js" +license = { text = "Apache-2.0" } +name = "handlebarz" +readme = "README.md" +requires-python = ">=3.10" +version = "0.1.0" + +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling"] + +[tool.hatch.build.targets.wheel] +packages = ["src/handlebarz"] diff --git a/py/packages/handlebarz/src/handlebarz/__init__.py b/py/packages/handlebarz/src/handlebarz/__init__.py new file mode 100644 index 000000000..e6078e237 --- /dev/null +++ b/py/packages/handlebarz/src/handlebarz/__init__.py @@ -0,0 +1,2 @@ +def hello() -> str: + return 'Hello from handlebarz!' diff --git a/py/packages/handlebarz/src/handlebarz/py.typed b/py/packages/handlebarz/src/handlebarz/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/py/pyproject.toml b/py/pyproject.toml index fad104ea0..f17e58c60 100644 --- a/py/pyproject.toml +++ b/py/pyproject.toml @@ -9,6 +9,7 @@ dependencies = [ "genkit-ollama-plugin", "genkit-pinecone-plugin", "genkit-vertex-ai-plugin", + "handlebarz", ] description = "Workspace for Genkit packages" license = { text = "Apache-2.0" } @@ -59,6 +60,7 @@ genkit-google-cloud-plugin = { workspace = true } genkit-ollama-plugin = { workspace = true } genkit-pinecone-plugin = { workspace = true } genkit-vertex-ai-plugin = { workspace = true } +handlebarz = { workspace = true } hello = { workspace = true } [tool.uv.workspace] diff --git a/py/uv.lock b/py/uv.lock index 51f6d9652..f7481742b 100644 --- a/py/uv.lock +++ b/py/uv.lock @@ -17,6 +17,7 @@ members = [ "genkit-pinecone-plugin", "genkit-vertex-ai-plugin", "genkit-workspace", + "handlebarz", "hello", ] @@ -436,6 +437,12 @@ wheels = [ name = "dotprompt" version = "0.1.0" source = { editable = "packages/dotprompt" } +dependencies = [ + { name = "handlebarz" }, +] + +[package.metadata] +requires-dist = [{ name = "handlebarz", editable = "packages/handlebarz" }] [[package]] name = "executing" @@ -469,6 +476,7 @@ name = "genkit" version = "0.1.0" source = { editable = "packages/genkit" } dependencies = [ + { name = "dotprompt" }, { name = "opentelemetry-api" }, { name = "opentelemetry-sdk" }, { name = "pydantic" }, @@ -477,6 +485,7 @@ dependencies = [ [package.metadata] requires-dist = [ + { name = "dotprompt", editable = "packages/dotprompt" }, { name = "opentelemetry-api", specifier = ">=1.29.0" }, { name = "opentelemetry-sdk", specifier = ">=1.29.0" }, { name = "pydantic", specifier = ">=2.10.5" }, @@ -578,6 +587,7 @@ dependencies = [ { name = "genkit-ollama-plugin" }, { name = "genkit-pinecone-plugin" }, { name = "genkit-vertex-ai-plugin" }, + { name = "handlebarz" }, ] [package.dev-dependencies] @@ -605,6 +615,7 @@ requires-dist = [ { name = "genkit-ollama-plugin", editable = "plugins/ollama" }, { name = "genkit-pinecone-plugin", editable = "plugins/pinecone" }, { name = "genkit-vertex-ai-plugin", editable = "plugins/vertex-ai" }, + { name = "handlebarz", editable = "packages/handlebarz" }, ] [package.metadata.requires-dev] @@ -882,6 +893,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, ] +[[package]] +name = "handlebarz" +version = "0.1.0" +source = { editable = "packages/handlebarz" } + [[package]] name = "hello" version = "0.1.0" From 7cb0116598b338c4e43a8c3136fadc4cf48a39da Mon Sep 17 00:00:00 2001 From: Michael Doyle Date: Fri, 31 Jan 2025 21:31:56 -0500 Subject: [PATCH 465/562] fix(js/plugins/firebase): properly await embedding promise (#1750) --- js/plugins/firebase/src/firestoreRetriever.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/js/plugins/firebase/src/firestoreRetriever.ts b/js/plugins/firebase/src/firestoreRetriever.ts index 97739a859..86b0194b7 100644 --- a/js/plugins/firebase/src/firestoreRetriever.ts +++ b/js/plugins/firebase/src/firestoreRetriever.ts @@ -122,10 +122,7 @@ export function defineFirestoreRetriever( collection: z.string().optional(), }), }, - async (input, options) => { - // Single embedding for text input - const embedding = await ai.embed({ embedder, content: input })[0] - .embedding; + async (content, options) => { if (!options.collection && !collection) { throw new Error( 'Must specify a collection to query in Firestore retriever.' @@ -137,6 +134,9 @@ export function defineFirestoreRetriever( for (const field in options.where || {}) { query = query.where(field, '==', options.where![field]); } + // Single embedding for text input + const embeddings = await ai.embed({ embedder, content }); + const embedding = embeddings[0].embedding; const result = await query .findNearest(vectorField, embedding, { limit: options.limit || 10, From c18b9654a685776dc273ebbbe75a731287ed2eaf Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:17:14 +0000 Subject: [PATCH 466/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.14 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 58f094ef2..d48bb37e5 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 6febe40aefbd75eb8d05a5b73bb5c238b6d853bb Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:17:16 +0000 Subject: [PATCH 467/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.14 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 0f15db6b7..39722b3f1 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From af711489ade9c1b2e01e474c15086a2b8caecf4d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:17:18 +0000 Subject: [PATCH 468/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.14 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index 29b9f0198..9becddbc0 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From 383d6e9c976a3b1a5ba4a32efc7b6640c8448fb8 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:01 +0000 Subject: [PATCH 469/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.14 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 8dd06cfb8..1f8bf2b45 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From 9d15141e84e683875b9a1f396769a57b83bc2a92 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:04 +0000 Subject: [PATCH 470/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.14 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index 2b987ef45..5bfc84686 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From 273260dc34979ea9d9c2f38876c35235f3d67e10 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:06 +0000 Subject: [PATCH 471/562] chore: bump genkit version to genkit@1.0.0-rc.14 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index f96141fb2..a009cdd49 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From 21eb6663814f6130f1e47676018fb1ca37156ed9 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:09 +0000 Subject: [PATCH 472/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.14 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index 82f415f69..b1cf29787 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From 00aa29016e7bb98fe44aea580f384bb31f1793c7 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:11 +0000 Subject: [PATCH 473/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.14 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 5374f59ce..06ea1a3da 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From 79656349d7e18acb1ad62e995fdfb5eb9d89cc1e Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:14 +0000 Subject: [PATCH 474/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.14 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 7c9697813..f50085b42 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From 744aa83d14e6baf0c6752e202aefbf00b3094bd9 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:17 +0000 Subject: [PATCH 475/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.14 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index f0c19a3d5..7eb319954 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From d48434ead371baa264d1fdb284b9a380f860dde3 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:19 +0000 Subject: [PATCH 476/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.14 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index 38bb49519..8cbf6593d 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From cc2e92d94651e488b12814f1581ce4efee4cc17b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:21 +0000 Subject: [PATCH 477/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.14 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 83c731978..5e3e2da86 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From b6b01bf8c501659461559a0a0ce5eaaf78007094 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:24 +0000 Subject: [PATCH 478/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.14 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index ece5f64b9..14217980c 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From 65ac5a0961a6e7d072b488182ccfbdb859c40990 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:27 +0000 Subject: [PATCH 479/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.14 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index 03f6f52e1..2029b2882 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From 224cceab4ff1076a03659d3f8c0a62c435719e35 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:29 +0000 Subject: [PATCH 480/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.14 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index 88caee4bf..62c157334 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From 69a5d73da17ed585633f673ffe8e4cd1274ddfa1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:32 +0000 Subject: [PATCH 481/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.14 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index a30186661..5e81a1cf5 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From 32b25a6bdef8093de0f9f5196076ce1ac441afc5 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:34 +0000 Subject: [PATCH 482/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.14 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index 82ed33d76..c210464f6 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From 6d3f4b21c9c4baf794f08acb361cb384cb4a8150 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:37 +0000 Subject: [PATCH 483/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.14 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index 885a9430f..b42e4d375 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From 6de1b10d55dd8242ddb4ec65cbcbed11b38a1ec9 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Sat, 1 Feb 2025 13:18:39 +0000 Subject: [PATCH 484/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.14 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index c15a459b9..8c055d8b0 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.13", + "version": "1.0.0-rc.14", "type": "commonjs", "scripts": { "check": "tsc", From e6adc0072492e6a2111d98d109991d52c8e9fa1d Mon Sep 17 00:00:00 2001 From: Yesudeep Mangalapilly Date: Sat, 1 Feb 2025 15:00:47 -0800 Subject: [PATCH 485/562] fix(ci): broken ci pipeline for python (#1778) --- .github/workflows/python.yml | 5 +- py/bin/run_tests | 16 ++++++ py/bin/setup | 49 +++++++++++++------ py/captainhook.json | 4 +- py/packages/dotprompt/pyproject.toml | 4 +- .../handlebarz/src/handlebarz/__init__.py | 17 +++++++ 6 files changed, 71 insertions(+), 24 deletions(-) create mode 100755 py/bin/run_tests diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 7a908ca1c..2c8b96fcb 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -6,12 +6,11 @@ jobs: python-checks: runs-on: ubuntu-latest env: - PATH: ${{ github.workspace }}/go/bin:${{ github.workspace }}/.cargo/bin:${{ github.workspace }}/.local/share/pnpm:${{ github.workspace }}/.local/bin:${{ env.PATH }} + PATH: ${{ github.workspace }}/go/bin:${{ github.workspace }}/.cargo/bin:${{ github.workspace }}/.local/share/pnpm:${{ github.workspace }}/.local/bin:/usr/local/bin:/usr/bin:/bin strategy: matrix: python-version: - "3.12" - - "3.13" defaults: run: working-directory: py @@ -35,7 +34,7 @@ jobs: run: uv run ruff check . - name: Run tests - run: uv run pytest . + run: ./bin/run_tests - name: Build documentation run: uv run mkdocs build --strict diff --git a/py/bin/run_tests b/py/bin/run_tests new file mode 100755 index 000000000..26a481e34 --- /dev/null +++ b/py/bin/run_tests @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# +# Run tests for all supported Python versions + +set -euo pipefail + +TOP_DIR=$(git rev-parse --show-toplevel) +PYTHON_VERSIONS=( + "3.12" + "3.13" +) + +for VERSION in "${PYTHON_VERSIONS[@]}"; do + echo "Running tests with Python ${VERSION}..." + uv run --python "python${VERSION}" --directory "${TOP_DIR}/py" pytest . +done diff --git a/py/bin/setup b/py/bin/setup index b6168b73c..dfb173e5f 100755 --- a/py/bin/setup +++ b/py/bin/setup @@ -15,6 +15,8 @@ # 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 if ((EUID == 0)) && [[ -z ${DANGEROUSLY_RUN_AS_ROOT+x} ]]; then echo "Please do not run as root unless DANGEROUSLY_RUN_AS_ROOT is set." @@ -63,11 +65,6 @@ function genkit::update_path() { # Remove trailing slash if present. new_path="${new_path%/}" - if [ ! -d "$new_path" ]; then - echo "Error: Directory $new_path does not exist" - return 1 - fi - # Check if path is already in PATH if [[ ":$PATH:" != *":$new_path:"* ]]; then if [ -n "${ZSH_VERSION:-}" ]; then @@ -100,26 +97,32 @@ function genkit::install_prerequisites() { # Darwin-based systems. brew install \ curl \ + fd \ gh \ go \ node \ - python3 + python3 \ + ripgrep elif [[ -x "$(command -v apt)" ]]; then # Debian-based systems. sudo apt install -y \ curl \ + fd-find \ gh \ golang \ nodejs \ - python3 + python3 \ + ripgrep elif [[ -x "$(command -v dnf)" ]]; then # Fedora-based systems. sudo dnf install -y \ curl \ + fd-find \ gh \ go \ node \ - python3 + python3 \ + ripgrep else echo "Unsupported OS. Please install protoc manually." fi @@ -154,30 +157,39 @@ function genkit::install_google_cloud_sdk() { } # Install all the required tools that have been written in Go. -function genkit::install_go_cli_tools() { +function genkit::install_go_cli_tools_ci() { + go install github.com/google/go-licenses@latest + go install golang.org/x/vuln/cmd/govulncheck@latest + go install oss.terrastruct.com/d2@latest +} + +# Install all the required tools that have been written in Go. +function genkit::install_go_cli_tools_eng() { go install github.com/Gelio/go-global-update@latest go install github.com/captainhook-go/captainhook/cmd/captainhook@latest go install github.com/google/addlicense@latest go install github.com/google/go-licenses@latest go install github.com/jesseduffield/lazygit@latest go install golang.org/x/vuln/cmd/govulncheck@latest + go install oss.terrastruct.com/d2@latest } # Install all the required tools that have been written in Rust. We're assuming # that the user has already installed rust and cargo. -function genkit::install_cargo_cli_tools() { +function genkit::install_cargo_cli_tools_eng() { cargo install --locked \ convco \ - fd-find \ - ripgrep \ rust-parallel \ taplo-cli } # Install NPM packages. function genkit::install_pnpm_cli_tools() { - # Install the Genkit CLI. See: https://firebase.google.com/docs/genkit/devtools - pnpm add -g genkit-cli + # Genkit CLI: https://firebase.google.com/docs/genkit/devtools + # Biome: https://biomejs.dev/ + pnpm add -g \ + @biomejs/biome \ + genkit-cli } # Install all the Python-related formatter and static analysis tools. @@ -189,6 +201,9 @@ function genkit::install_python_cli_tools() { # Install documentation site generator. function genkit::install_docs_cli_tools() { + # Install d2. + curl -fsSL https://d2lang.com/install.sh | sh -s -- + # Engineering documentation site generator. # See: https://squidfunk.github.io/mkdocs-material/ uv tool install \ @@ -218,8 +233,6 @@ function genkit::setup_genkit() { # Install all the common packages. function genkit::install_common_packages() { genkit::install_prerequisites - genkit::install_go_cli_tools - genkit::install_cargo_cli_tools genkit::install_python_cli_tools genkit::install_docs_cli_tools genkit::install_pnpm_cli_tools @@ -227,12 +240,16 @@ function genkit::install_common_packages() { # Install all the required tools for CI. function genkit::install_ci_packages() { + export PNPM_HOME="$HOME/.local/share/pnpm" genkit::install_common_packages + genkit::install_go_cli_tools_ci } # Install all the required tools for engineering. function genkit::install_eng_packages() { genkit::install_common_packages + genkit::install_go_cli_tools_eng + genkit::install_cargo_cli_tools_eng genkit::install_google_cloud_sdk genkit::install_pre_commit_hooks genkit::setup_genkit diff --git a/py/captainhook.json b/py/captainhook.json index b802ec9fe..78234a94a 100644 --- a/py/captainhook.json +++ b/py/captainhook.json @@ -40,7 +40,7 @@ "run": "uvx --directory py ruff check --fix ." }, { - "run": "uv run --directory py pytest ." + "run": "py/bin/run_tests" }, { "run": "uv run --directory py mkdocs build" @@ -88,7 +88,7 @@ "run": "uvx --directory py ruff check --fix ." }, { - "run": "uv run --directory py pytest ." + "run": "py/bin/run_tests" }, { "run": "uv run --directory py mkdocs build" diff --git a/py/packages/dotprompt/pyproject.toml b/py/packages/dotprompt/pyproject.toml index f469a201d..5387b1d72 100644 --- a/py/packages/dotprompt/pyproject.toml +++ b/py/packages/dotprompt/pyproject.toml @@ -14,9 +14,7 @@ classifiers = [ "Topic :: Scientific/Engineering :: Artificial Intelligence", "Topic :: Software Development :: Libraries", ] -dependencies = [ - "handlebarz", -] +dependencies = ["handlebarz"] description = "Dotprompt is a language-neutral executable prompt template file format for Generative AI." license = { text = "Apache-2.0" } name = "dotprompt" diff --git a/py/packages/handlebarz/src/handlebarz/__init__.py b/py/packages/handlebarz/src/handlebarz/__init__.py index e6078e237..7b5fdf8b2 100644 --- a/py/packages/handlebarz/src/handlebarz/__init__.py +++ b/py/packages/handlebarz/src/handlebarz/__init__.py @@ -1,2 +1,19 @@ +# 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 + + def hello() -> str: return 'Hello from handlebarz!' From 27104c3ccd730a7fd4ae84b9816b54a2ad684379 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Sat, 1 Feb 2025 18:32:36 -0500 Subject: [PATCH 486/562] chore: added js/testapps/personal-testing to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e5ec5dee5..1b352ed55 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ js/testapps/rag/*.json !js/testapps/rag/package.json js/testapps/evals/__db*.json js/testapps/menu/**/__db*.json +js/testapps/personal-testing # Test files last_recording.mp4 From d4e9a8a3f837c5c5c377a77f987e92e7ae0500e1 Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Sun, 2 Feb 2025 20:54:33 -0500 Subject: [PATCH 487/562] chore: update school agent sample to 1.0 (#1729) * chore: update school agent sample to 1.0 * Update samples/js-schoolAgent/package.json Co-authored-by: Pavel Jbanov * Update samples/js-schoolAgent/package.json Co-authored-by: Pavel Jbanov --------- Co-authored-by: Pavel Jbanov --- samples/js-schoolAgent/README.md | 6 +- samples/js-schoolAgent/package-lock.json | 769 +++++++++++++++++------ samples/js-schoolAgent/package.json | 6 +- samples/js-schoolAgent/src/genkit.ts | 2 +- 4 files changed, 582 insertions(+), 201 deletions(-) diff --git a/samples/js-schoolAgent/README.md b/samples/js-schoolAgent/README.md index 35d91f3ab..dd8b6a76a 100644 --- a/samples/js-schoolAgent/README.md +++ b/samples/js-schoolAgent/README.md @@ -47,13 +47,13 @@ NOTE: The agent description is how the generalized agent knows what tools the sp npm install ``` -2. Set up your Google AI API key: +1. Set up your Google AI API key: ```bash export GOOGLE_GENAI_API_KEY=your_api_key_here ``` -3. Start the development server: +1. Start the development server: ```bash npm run genkit:dev @@ -61,7 +61,7 @@ npm run genkit:dev In your terminal, a commandline chat interface should show up: -``` +```terminal Telemetry API running on http://localhost:4033 Genkit Developer UI: http://localhost:4000 diff --git a/samples/js-schoolAgent/package-lock.json b/samples/js-schoolAgent/package-lock.json index 879ce12f7..919abbffb 100644 --- a/samples/js-schoolAgent/package-lock.json +++ b/samples/js-schoolAgent/package-lock.json @@ -9,16 +9,16 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@genkit-ai/googleai": "^0.9.3", - "genkit": "^0.9.3", - "google-auth-library": "^9.6.3", + "@genkit-ai/googleai": "^1.0.0-rc.12", + "genkit": "^1.0.0-rc.12", + "google-auth-library": "^1.0.0-rc.12", "llm-chunk": "^0.0.1", "pdf-parse": "^1.1.1" }, "devDependencies": { "@types/pdf-parse": "^1.1.4", "cross-env": "^7.0.3", - "genkit-cli": "^0.9.3", + "genkit-cli": "^1.0.0-rc.12", "rimraf": "^6.0.1", "tsx": "^4.19.1", "typescript": "^5.3.3" @@ -441,14 +441,15 @@ } }, "node_modules/@genkit-ai/ai": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-0.9.12.tgz", - "integrity": "sha512-xyVVAIGKNpj5zCkoEfWZkzwctl0/hmpX6vKZgdgMH2MiqP5LzTp7rUekBMon8c1rMDVAze97QVSjAmZIoMLSlA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.12.tgz", + "integrity": "sha512-1hofDfuTEVDfryy7klgeJwCyXOp4wEZ+VPU34Dts9S5PYoN1nb23KAhnz+6dHgM42REM1PI5Zh2Wq8O10BXIww==", "dependencies": { - "@genkit-ai/core": "0.9.12", + "@genkit-ai/core": "1.0.0-rc.12", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.11.19", "colorette": "^2.0.20", + "dotprompt": "^1.0.0-dev.3 || ^1", "json5": "^2.2.3", "node-fetch": "^3.3.2", "partial-json": "^0.1.7", @@ -456,9 +457,9 @@ } }, "node_modules/@genkit-ai/core": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-0.9.12.tgz", - "integrity": "sha512-QPJZ3TL5Iq2fyeo30MpUjd3ZLcYQf97RsitDZhMbGy3vMwbgig0nhEbJ6v/qaWsOMqSfIxJE/gETY3mMts1vRg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.12.tgz", + "integrity": "sha512-3K7GVXR1vnJu2ANICbojxKpBoxIncuGIvn0NJ7T+s3/IOBQcusmkUyJoc4TdeRsZdX693J+oMUSx+ZE18s28eQ==", "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -466,11 +467,13 @@ "@opentelemetry/sdk-metrics": "^1.25.0", "@opentelemetry/sdk-node": "^0.52.0", "@opentelemetry/sdk-trace-base": "^1.25.0", + "@types/json-schema": "^7.0.15", "ajv": "^8.12.0", "ajv-formats": "^3.0.1", "async-mutex": "^0.5.0", "body-parser": "^1.20.3", "cors": "^2.8.5", + "dotprompt": "^1.0.0-dev.3 || ^1", "express": "^4.21.0", "get-port": "^5.1.0", "json-schema": "^0.4.0", @@ -478,39 +481,174 @@ "zod-to-json-schema": "^3.22.4" } }, - "node_modules/@genkit-ai/dotprompt": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/dotprompt/-/dotprompt-0.9.12.tgz", - "integrity": "sha512-eEHBRzRVemiPuqCBbXiLgltNWpmCHmC+gVHBhsAnrfOYBlwmPvh2nnAPBXGYnkDH87PuN11jg8YJQmO4kQuoSw==", - "dependencies": { - "@genkit-ai/ai": "0.9.12", - "@genkit-ai/core": "0.9.12", - "front-matter": "^4.0.2", - "handlebars": "^4.7.8", - "node-fetch": "^3.3.2" - } - }, "node_modules/@genkit-ai/googleai": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-0.9.12.tgz", - "integrity": "sha512-q6bX9Nq4xVpzH4vd9W+rg4xO7SVodG516K4BVM04DtxU5w0ogwLl/mRPEU0VIoISjD6FwaC4sPjPkEerTp8a4g==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-1.0.0-rc.12.tgz", + "integrity": "sha512-P56jclocNjgwSBizjieMc99D7WJYXAAaI7WOjkuij7oyt6BOvzCZ9rcuTJBtH/n3PRCD1P0ixt8l3m6SrxYjNQ==", "dependencies": { "@google/generative-ai": "^0.21.0", "google-auth-library": "^9.6.3", "node-fetch": "^3.3.2" }, "peerDependencies": { - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.12" + } + }, + "node_modules/@genkit-ai/googleai/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@genkit-ai/googleai/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@genkit-ai/googleai/node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@genkit-ai/googleai/node_modules/gaxios/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@genkit-ai/googleai/node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@genkit-ai/googleai/node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@genkit-ai/googleai/node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@genkit-ai/googleai/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@genkit-ai/googleai/node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@genkit-ai/googleai/node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@genkit-ai/googleai/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==" + }, + "node_modules/@genkit-ai/googleai/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" } }, "node_modules/@genkit-ai/telemetry-server": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-0.9.12.tgz", - "integrity": "sha512-8w1O9LOUtMCGc8bx+Vcd5D68L8ctM6H544Vxnfvy8gx4JHV5dj7vQiI9YDgm65pgP15pytQzt1TcRtOtYoimFw==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.12.tgz", + "integrity": "sha512-hP1qQrFkarq3+A4KQITjoGaw52MBOGKIkRiqr9HbYYXd8KOQiJ7KNo9Hv7VixphG/WQ700RUMvqjB9H4DzXZyg==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", - "@genkit-ai/tools-common": "0.9.12", + "@genkit-ai/tools-common": "1.0.0-rc.12", "@google-cloud/firestore": "^7.6.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -524,14 +662,16 @@ } }, "node_modules/@genkit-ai/tools-common": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-0.9.12.tgz", - "integrity": "sha512-A9ToM/CY6vxcBSw4o47Q7qrpMaRsk4P6ZuzoDMlCzCVI+RZN5ulTL4LXMrq6nQ/KxY1GT94bSoulROXBMzW/Gw==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.12.tgz", + "integrity": "sha512-N6qvS92Xt2qqnsqoQfuKveQd+4bBeRhEWlwP0e99tfmcFSfL4MDj7JLvfaoNq2GGkr4xBURQjB9NGtojZksyNg==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", "@trpc/server": "10.45.0", "adm-zip": "^0.5.12", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", "axios": "^1.7.7", "body-parser": "^1.20.2", "chokidar": "^3.5.3", @@ -554,24 +694,6 @@ "zod-to-json-schema": "^3.22.4" } }, - "node_modules/@genkit-ai/tools-common/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==", - "dev": true - }, - "node_modules/@genkit-ai/tools-common/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@genkit-ai/tools-common/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -1700,6 +1822,11 @@ "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, "node_modules/@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", @@ -1778,7 +1905,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dev": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -1827,11 +1953,14 @@ } }, "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dependencies": { + "es6-promisify": "^5.0.0" + }, "engines": { - "node": ">= 14" + "node": ">= 4.0.0" } }, "node_modules/ajv": { @@ -1916,12 +2045,10 @@ } }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/array-flatten": { "version": "1.1.1", @@ -1945,14 +2072,12 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { "version": "1.7.9", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", - "dev": true, "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -2179,9 +2304,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==" + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==" }, "node_modules/cli-cursor": { "version": "3.1.0", @@ -2324,7 +2449,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -2484,7 +2608,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -2527,6 +2650,15 @@ "node": ">=8" } }, + "node_modules/dotprompt": { + "version": "1.0.0-dev.3", + "resolved": "https://registry.npmjs.org/dotprompt/-/dotprompt-1.0.0-dev.3.tgz", + "integrity": "sha512-gAcIhG+vzZlrZcBN/lO8gEMUtXHKMLbs1+snJuZrPkZnNgnWrzz1hYN4vYotpng/5fHsYSPUEckECy6qUNSrsQ==", + "dependencies": { + "handlebars": "^4.7.8", + "yaml": "^2.5.0" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2632,6 +2764,19 @@ "node": ">= 0.4" } }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, "node_modules/esbuild": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", @@ -2693,18 +2838,6 @@ "node": ">=0.8.0" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -2732,7 +2865,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "dev": true, "engines": { "node": ">=6" } @@ -2955,7 +3087,6 @@ "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "dev": true, "funding": [ { "type": "individual", @@ -2991,7 +3122,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -3034,14 +3164,6 @@ "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", "dev": true }, - "node_modules/front-matter": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", - "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", - "dependencies": { - "js-yaml": "^3.13.1" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3071,18 +3193,14 @@ "dev": true }, "node_modules/gaxios": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", - "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", + "integrity": "sha512-BoENMnu1Gav18HcpV9IleMPZ9exM+AvUjrAOV4Mzs/vfz2Lu/ABv451iEXByKiMPn2M140uul1txXCg83sAENw==", "dependencies": { + "abort-controller": "^3.0.0", "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "is-stream": "^2.0.0", - "node-fetch": "^2.6.9", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=14" + "https-proxy-agent": "^2.2.1", + "node-fetch": "^2.3.0" } }, "node_modules/gaxios/node_modules/node-fetch": { @@ -3104,49 +3222,66 @@ } } }, - "node_modules/gaxios/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/gcp-metadata": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.6.3.tgz", + "integrity": "sha512-MSmczZctbz91AxCvqp9GHBoZOSbJKAICV7Ow/AIWSJZRrRchUd5NL1b2P4OfP+4m490BEUPhhARfpHdqCxuCvg==", + "dependencies": { + "axios": "^0.18.0", + "extend": "^3.0.1", + "retry-axios": "0.3.2" + }, + "engines": { + "node": ">=4" } }, - "node_modules/gcp-metadata": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", - "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "node_modules/gcp-metadata/node_modules/axios": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", + "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", + "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410", "dependencies": { - "gaxios": "^6.0.0", - "json-bigint": "^1.0.0" + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + } + }, + "node_modules/gcp-metadata/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/gcp-metadata/node_modules/follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dependencies": { + "debug": "=3.1.0" }, "engines": { - "node": ">=14" + "node": ">=4.0" } }, "node_modules/genkit": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/genkit/-/genkit-0.9.12.tgz", - "integrity": "sha512-m1VQE/yhuii0y1aGTnkoSesSXTNE25q1s7vv5YVgJWa/t2gOXuznZOoHTJ847f/3mKC7fgnV7xGI+t/+7wbe0g==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.12.tgz", + "integrity": "sha512-IsL8erWfFraJGENqYf+StJakoAkfsq4VHLhQkSU5iBNYways3BumsP1KT5CKNK3lpHGJWg65LrGznAxpJ5xTqQ==", "dependencies": { - "@genkit-ai/ai": "0.9.12", - "@genkit-ai/core": "0.9.12", - "@genkit-ai/dotprompt": "0.9.12", + "@genkit-ai/ai": "1.0.0-rc.12", + "@genkit-ai/core": "1.0.0-rc.12", "uuid": "^10.0.0" } }, "node_modules/genkit-cli": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-0.9.12.tgz", - "integrity": "sha512-Qsk7TmVBxghTT+0cRe2j0fYJboUIfWhuUoeVbWshranye6tConGMyDEZdXBIeMWIixSbG7k8a7a2INdRdjcQJw==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.12.tgz", + "integrity": "sha512-itEg/RDIs7CW9Oho7K0sb0luERwwksxkJvAkstINCroK8s9tkoDQAPlROKeV830sMeP9kze74EG2nySsOm6fvg==", "dev": true, "dependencies": { - "@genkit-ai/telemetry-server": "0.9.12", - "@genkit-ai/tools-common": "0.9.12", + "@genkit-ai/telemetry-server": "1.0.0-rc.12", + "@genkit-ai/tools-common": "1.0.0-rc.12", "axios": "^1.7.7", "colorette": "^2.0.20", "commander": "^11.1.0", @@ -3274,19 +3409,49 @@ } }, "node_modules/google-auth-library": { - "version": "9.15.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", - "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-1.6.1.tgz", + "integrity": "sha512-jYiWC8NA9n9OtQM7ANn0Tk464do9yhKEtaJ72pKcaBiEwn4LwcGYIYOfwtfsSm3aur/ed3tlSxbmg24IAT6gAg==", "dependencies": { - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "gaxios": "^6.1.1", - "gcp-metadata": "^6.1.0", - "gtoken": "^7.0.0", - "jws": "^4.0.0" + "axios": "^0.18.0", + "gcp-metadata": "^0.6.3", + "gtoken": "^2.3.0", + "jws": "^3.1.5", + "lodash.isstring": "^4.0.1", + "lru-cache": "^4.1.3", + "retry-axios": "^0.3.2" }, "engines": { - "node": ">=14" + "node": ">=4" + } + }, + "node_modules/google-auth-library/node_modules/axios": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", + "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", + "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410", + "dependencies": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + } + }, + "node_modules/google-auth-library/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/google-auth-library/node_modules/follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dependencies": { + "debug": "=3.1.0" + }, + "engines": { + "node": ">=4.0" } }, "node_modules/google-gax": { @@ -3312,6 +3477,132 @@ "node": ">=14" } }, + "node_modules/google-gax/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/google-gax/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/google-gax/node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dev": true, + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "dev": true, + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "dev": true, + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dev": true, + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/google-gax/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/google-gax/node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dev": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/google-gax/node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dev": true, + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/google-gax/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==", + "dev": true + }, "node_modules/google-gax/node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -3345,6 +3636,27 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/google-p12-pem": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.5.tgz", + "integrity": "sha512-50rTrqYPTPPwlu9TNl/HkJbBENEpbRzTOVLFJ4YWM86njZgXHFy+FP+tLRSd9m132Li9Dqi27Z3KIWDEv5y+EA==", + "deprecated": "Package is no longer maintained", + "dependencies": { + "node-forge": "^0.10.0", + "pify": "^4.0.0" + }, + "bin": { + "gp12-pem": "build/src/bin/gp12-pem.js" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -3363,15 +3675,18 @@ "dev": true }, "node_modules/gtoken": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", - "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", + "integrity": "sha512-EaB49bu/TCoNeQjhCYKI/CurooBKkGxIqFHsWABW0b25fobBYVTMe84A8EBVVZhl8emiUdNypil9huMOTmyAnw==", "dependencies": { - "gaxios": "^6.0.0", - "jws": "^4.0.0" + "gaxios": "^1.0.4", + "google-p12-pem": "^1.0.0", + "jws": "^3.1.5", + "mime": "^2.2.0", + "pify": "^4.0.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=6.0.0" } }, "node_modules/handlebars": { @@ -3490,31 +3805,23 @@ "dev": true }, "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "agent-base": "^4.3.0", + "debug": "^3.1.0" }, "engines": { - "node": ">= 14" + "node": ">= 4.5.0" } }, "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "ms": "^2.1.1" } }, "node_modules/https-proxy-agent/node_modules/ms": { @@ -3630,6 +3937,28 @@ "node": ">=8" } }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -3760,12 +4089,12 @@ } }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -3814,9 +4143,9 @@ } }, "node_modules/jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -3824,11 +4153,11 @@ } }, "node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "dependencies": { - "jwa": "^2.0.0", + "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, @@ -3854,6 +4183,11 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3904,10 +4238,13 @@ "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==" }, "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } }, "node_modules/make-dir": { "version": "3.1.0", @@ -3972,14 +4309,14 @@ } }, "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "bin": { "mime": "cli.js" }, "engines": { - "node": ">=4" + "node": ">=4.0.0" } }, "node_modules/mime-db": { @@ -4111,6 +4448,14 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4294,6 +4639,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", @@ -4351,6 +4702,14 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "engines": { + "node": ">=6" + } + }, "node_modules/proto3-json-serializer": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", @@ -4401,8 +4760,7 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/ps-tree": { "version": "1.2.0", @@ -4419,6 +4777,11 @@ "node": ">= 0.10" } }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + }, "node_modules/pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -4588,6 +4951,14 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/retry-axios": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-0.3.2.tgz", + "integrity": "sha512-jp4YlI0qyDFfXiXGhkCOliBN1G7fRH03Nqy8YdShzGqbY5/9S2x/IR6C88ls2DFkbWuL3ASkP7QD3pVrNpPgwQ==", + "peerDependencies": { + "axios": "*" + } + }, "node_modules/retry-request": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", @@ -4792,6 +5163,17 @@ "node": ">= 0.8" } }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4951,11 +5333,6 @@ "node": "*" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -5555,11 +5932,15 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" + }, "node_modules/yaml": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", - "dev": true, "bin": { "yaml": "bin.mjs" }, diff --git a/samples/js-schoolAgent/package.json b/samples/js-schoolAgent/package.json index 2ab17b892..ce2d821e5 100644 --- a/samples/js-schoolAgent/package.json +++ b/samples/js-schoolAgent/package.json @@ -16,14 +16,14 @@ "author": "", "license": "ISC", "dependencies": { - "genkit": "^0.9.3", - "@genkit-ai/googleai": "^0.9.3", + "genkit": "^1.0.0-rc.14", + "@genkit-ai/googleai": "^1.0.0-rc.14", "google-auth-library": "^9.6.3", "llm-chunk": "^0.0.1", "pdf-parse": "^1.1.1" }, "devDependencies": { - "genkit-cli": "^0.9.3", + "genkit-cli": "^1.0.0-rc.14", "@types/pdf-parse": "^1.1.4", "cross-env": "^7.0.3", "rimraf": "^6.0.1", diff --git a/samples/js-schoolAgent/src/genkit.ts b/samples/js-schoolAgent/src/genkit.ts index 025c4b30d..70894085d 100644 --- a/samples/js-schoolAgent/src/genkit.ts +++ b/samples/js-schoolAgent/src/genkit.ts @@ -15,7 +15,7 @@ */ import { gemini15Pro, googleAI } from '@genkit-ai/googleai'; -import { genkit } from 'genkit'; +import { genkit } from 'genkit/beta'; import { AgentState } from './types'; export const ai = genkit({ From 3ebddd9970eacaf04db5f0799939e2807b863c11 Mon Sep 17 00:00:00 2001 From: Yesudeep Mangalapilly Date: Mon, 3 Feb 2025 05:43:34 -0800 Subject: [PATCH 488/562] fix(editorconfig): misconfiguration that would cause editors to format incorrectly #1786 (#1787) --- .editorconfig | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 5686b442a..42b59f896 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,9 +3,21 @@ root = true [*] charset = utf-8 -indent_style = space +end_of_line = lf indent_size = 2 +indent_style = space insert_final_newline = true -trim_trailing_whitespace = true max_line_length = 80 +trim_trailing_whitespace = true + +[*.{js,jsx,ts,tsx}] quote_type = single + +[*.py] +indent_size = 4 +indent_style = space +quote_type = single + +[{Makefile,go.mod,go.sum,*.go,.gitmodules}] +indent_size = 4 +indent_style = tab From 60af9d9336b468f9d9703231ab966c08debdbe35 Mon Sep 17 00:00:00 2001 From: Yesudeep Mangalapilly Date: Mon, 3 Feb 2025 05:43:49 -0800 Subject: [PATCH 489/562] test: update pytest runner to also output coverage report and set min threshold to 70% #1714 (#1790) --- py/bin/run_tests | 8 ++++---- py/pyproject.toml | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/py/bin/run_tests b/py/bin/run_tests index 26a481e34..ffb64b9e1 100755 --- a/py/bin/run_tests +++ b/py/bin/run_tests @@ -6,11 +6,11 @@ set -euo pipefail TOP_DIR=$(git rev-parse --show-toplevel) PYTHON_VERSIONS=( - "3.12" - "3.13" + "3.12" + "3.13" ) for VERSION in "${PYTHON_VERSIONS[@]}"; do - echo "Running tests with Python ${VERSION}..." - uv run --python "python${VERSION}" --directory "${TOP_DIR}/py" pytest . + echo "Running tests with Python ${VERSION}..." + uv run --python "python${VERSION}" --directory "${TOP_DIR}/py" pytest . done diff --git a/py/pyproject.toml b/py/pyproject.toml index f17e58c60..b9cb5b853 100644 --- a/py/pyproject.toml +++ b/py/pyproject.toml @@ -46,6 +46,12 @@ python_files = [ ] testpaths = ["packages", "plugins", "samples"] +[tool.pytest.ini_options] +addopts = "--cov" + +[tool.coverage.report] +fail_under = 70 + # uv based package management. [tool.uv] default-groups = ["dev", "lint"] From fa86e195f0e4a118eade4513218ff5418b72266f Mon Sep 17 00:00:00 2001 From: Yesudeep Mangalapilly Date: Mon, 3 Feb 2025 05:44:04 -0800 Subject: [PATCH 490/562] chore(config): automatically label PRs based on modifications to save eng time #1720 (#1789) --- .github/labeler.yml | 105 ++++++++++++++++++++++++++++++++++ .github/workflows/labeler.yml | 13 +++++ 2 files changed, 118 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/labeler.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 000000000..e71752dc3 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,105 @@ +# Add 'root' label to any root file changes +# Quotation marks are required for the leading asterisk +root: + - changed-files: + - any-glob-to-any-file: '*' + +docs: + - changed-files: + - any-glob-to-any-file: + - '**/*.md' + - '**/*.rst' + - 'LICENSE' + - 'docs-go/**' + - 'docs/**' + - 'py/engdocs/**' + +go: + - changed-files: + - any-glob-to-any-file: + - '**/*.go' + - '**/go.mod' + - '**/go.sum' + - 'go/**' + +python: + - changed-files: + - any-glob-to-any-file: + - '**/*.py' + - '**/pyproject.toml' + - 'py/**' + +js: + - changed-files: + - any-glob-to-any-file: + - '**/*.js' + - '**/*.jsx' + - '**/*.ts' + - '**/*.tsx' + - '**/package.json' + - 'js/**' + +tooling: + - changed-files: + - any-glob-to-any-file: + - 'genkit-tools/**' + +config: + - changed-files: + - any-glob-to-any-file: + - '**/*.toml' + - '**/*.yaml' + - '**/*.yml' + - '**/.editorconfig' + - '**/.github/**' + - '**/.gitignore' + - '**/.npmignore' + - '**/.npmrc' + - '**/.prettierignore' + - '**/package.json' + - '**/tsconfig.*.json' + - '**/tsconfig.json' + - '**/typedoc.json' + +sample: + - changed-files: + - any-glob-to-any-file: + - 'samples/**' + +dotprompt: + - changed-files: + - any-glob-to-any-file: + - '**/dotprompt/**' + +handlebarz: + - changed-files: + - any-glob-to-any-file: + - '**/handlebarz/**' + +# Automatically add labels to any PR also based on branch naming conventions. +build: + - head-branch: [^.*/build/.*, build] + +chore: + - head-branch: [^.*/chore/.*, chore] + +ci: + - head-branch: [^.*/ci/.*, ci] + +feature: + - head-branch: [^.*/feat/.*, feature] + +fix: + - head-branch: [^.*/fix/.*, fix] + +perf: + - head-branch: [^.*/perf/.*, perf] + +refactor: + - head-branch: [^.*/refactor/.*, refactor] + +style: + - head-branch: [^.*/style/.*, style] + +test: + - head-branch: [^.*/test/.*, test] diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 000000000..09cf4ca17 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,13 @@ +name: "Pull Request Labeler" + +on: +- pull_request_target + +jobs: + labeler: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v5 From d55e41bade223603d33c6bfdb2ef4e6f297d5d54 Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Mon, 3 Feb 2025 09:31:59 -0500 Subject: [PATCH 491/562] feat: add IDX support to other samples (#1746) * feat: add IDX support to other samples Co-authored-by: Michael Doyle --------- Co-authored-by: Michael Doyle --- samples/README.md | 9 + samples/idx-template.json | 23 + samples/idx-template.nix | 10 + samples/js-angular/package.json | 2 +- samples/js-character-generator/.idx/dev.nix | 11 +- samples/js-character-generator/package.json | 2 +- samples/js-coffee-shop/.idx/dev.nix | 43 ++ samples/js-coffee-shop/README.md | 10 +- samples/js-schoolAgent/.idx/dev.nix | 43 ++ samples/js-schoolAgent/package-lock.json | 662 ++++---------------- samples/js-schoolAgent/package.json | 2 +- 11 files changed, 269 insertions(+), 548 deletions(-) create mode 100644 samples/idx-template.json create mode 100644 samples/idx-template.nix create mode 100644 samples/js-coffee-shop/.idx/dev.nix create mode 100644 samples/js-schoolAgent/.idx/dev.nix diff --git a/samples/README.md b/samples/README.md index c106a1468..9fea2a61d 100644 --- a/samples/README.md +++ b/samples/README.md @@ -10,3 +10,12 @@ Take a look at some samples of Genkit in use: - [js-angular](js-angular/): Demo of streaming to an Angular frontend - [js-schoolAgent](js-schoolAgent/): A simple school assistant system with a routing agent and specialized agents - [prompts](prompts/): Shows off several prompting techniques + +These are also available in IDX, Google's Cloud-Based IDE, for you to try. + + + Try in IDX + diff --git a/samples/idx-template.json b/samples/idx-template.json new file mode 100644 index 000000000..64fa3a14c --- /dev/null +++ b/samples/idx-template.json @@ -0,0 +1,23 @@ +{ + "name": "Genkit Samples", + "description": "Genkit Samples", + "categories": ["GenAI"], + "icon": "https://www.gstatic.com/monospace/240513/logo_firebase.svg", + "publisher": "Firebase", + "host": { + "virtualization": true + }, + "params": [ + { + "id": "sample", + "name": "Sample", + "type": "enum", + "default": "js-character-generator", + "options": { + "js-character-generator": "Simple Character Generator", + "js-coffee-shop": "Coffeeshop w Several types of Prompt", + "js-schoolAgent": "A Multi-Agent School Assistant" + } + } + ] +} diff --git a/samples/idx-template.nix b/samples/idx-template.nix new file mode 100644 index 000000000..7c95b084f --- /dev/null +++ b/samples/idx-template.nix @@ -0,0 +1,10 @@ +{pkgs, sample ? "js-character-generator", ...}: { + packages = [ + pkgs.nodejs + ]; + bootstrap = '' + mkdir "$out" + cp -rf ${./.}/${sample}/. "$out" + chmod -R u+w "$out" + ''; +} \ No newline at end of file diff --git a/samples/js-angular/package.json b/samples/js-angular/package.json index 07f36dae4..07907ad2a 100644 --- a/samples/js-angular/package.json +++ b/samples/js-angular/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "description": "This is a simple UI for streaming RPG character generator.", "scripts": { - "start": "concurrently npm:start:server npm:start:ng", + "genkit:dev": "concurrently npm:start:server npm:start:ng", "start:server": "cd server && npm run genkit:dev", "start:ng": "cd genkit-app && npm run start", "setup": "npm i && cd server && npm i && cd ../genkit-app && npm i" diff --git a/samples/js-character-generator/.idx/dev.nix b/samples/js-character-generator/.idx/dev.nix index 328c569d2..868601c41 100644 --- a/samples/js-character-generator/.idx/dev.nix +++ b/samples/js-character-generator/.idx/dev.nix @@ -12,10 +12,9 @@ # Sets environment variables in the workspace env = { #TODO Get a API key from https://g.co/ai/idxGetGeminiKey - GOOGLE_GENAI_API_KEY = "TODO"; + GOOGLE_GENAI_API_KEY = ""; }; idx = { - internal.templates-cli.enable = true; # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" extensions = [ # "vscodevim.vim" @@ -31,7 +30,13 @@ }; # Runs when the workspace is (re)started onStart = { - run-server = "npm run dev"; + run-server = "if [ -z \"\${GOOGLE_GENAI_API_KEY}\" ]; then \ + echo 'No Gemini API key detected, enter a Gemini API key from https://aistudio.google.com/app/apikey:' && \ + read -s GOOGLE_GENAI_API_KEY && \ + echo 'You can also add to .idx/dev.nix to automatically add to your workspace' + export GOOGLE_GENAI_API_KEY; \ + fi && \ + npm run genkit:dev"; }; }; }; diff --git a/samples/js-character-generator/package.json b/samples/js-character-generator/package.json index 859f0cfb6..79ce42513 100644 --- a/samples/js-character-generator/package.json +++ b/samples/js-character-generator/package.json @@ -4,7 +4,7 @@ "description": "A simple Firebase Genkit app to generate fantasy characters", "main": "index.js", "scripts": { - "dev": "genkit start -- npx tsx --watch index.ts" + "genkit:dev": "genkit start -- npx tsx --watch index.ts" }, "keywords": [], "author": "", diff --git a/samples/js-coffee-shop/.idx/dev.nix b/samples/js-coffee-shop/.idx/dev.nix new file mode 100644 index 000000000..147aee08e --- /dev/null +++ b/samples/js-coffee-shop/.idx/dev.nix @@ -0,0 +1,43 @@ +## Default Nix Environment for Typescript + Gemini Examples +## Requires the sample to be started with npx run genkit:dev + +# To learn more about how to use Nix to configure your environment +# see: https://developers.google.com/idx/guides/customize-idx-env +{ pkgs, ... }: { + # Which nixpkgs channel to use. + channel = "stable-24.05"; # or "unstable" + # Use https://search.nixos.org/packages to find packages + packages = [ + pkgs.nodejs_20 + pkgs.util-linux + ]; + # Sets environment variables in the workspace + env = { + #TODO Get a API key from https://g.co/ai/idxGetGeminiKey + GOOGLE_GENAI_API_KEY = ""; + }; + idx = { + # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" + extensions = [ + ]; + + # Workspace lifecycle hooks + workspace = { + # Runs when a workspace is first created + onCreate = { + npm-install = "npm ci --no-audit --prefer-offline --no-progress --timing"; + default.openFiles = [ "README.md" "src/index.ts" ]; + }; + # Runs when the workspace is (re)started + onStart = { + run-server = "if [ -z \"\${GOOGLE_GENAI_API_KEY}\" ]; then \ + echo 'No Gemini API key detected, enter a Gemini API key from https://aistudio.google.com/app/apikey:' && \ + read -s GOOGLE_GENAI_API_KEY && \ + echo 'You can also add to .idx/dev.nix to automatically add to your workspace' + export GOOGLE_GENAI_API_KEY; \ + fi && \ + npm run genkit:dev"; + }; + }; + }; +} \ No newline at end of file diff --git a/samples/js-coffee-shop/README.md b/samples/js-coffee-shop/README.md index 79958cd6a..d5de16b93 100644 --- a/samples/js-coffee-shop/README.md +++ b/samples/js-coffee-shop/README.md @@ -8,17 +8,17 @@ npm run genkit:dev or try it out on Project IDX, our Cloud-based IDE - + + srcset="https://cdn.idx.dev/btn/try_dark_32.svg"> + srcset="https://cdn.idx.dev/btn/try_light_32.svg"> Open in IDX + alt="Try in IDX" + src="https://cdn.idx.dev/btn/try_purple_32.svg"> diff --git a/samples/js-schoolAgent/.idx/dev.nix b/samples/js-schoolAgent/.idx/dev.nix new file mode 100644 index 000000000..ce5807066 --- /dev/null +++ b/samples/js-schoolAgent/.idx/dev.nix @@ -0,0 +1,43 @@ +## Default Nix Environment for Typescript + Gemini Examples +## Requires the sample to be started with npx run genkit:dev + +# To learn more about how to use Nix to configure your environment +# see: https://developers.google.com/idx/guides/customize-idx-env +{ pkgs, ... }: { + # Which nixpkgs channel to use. + channel = "stable-24.05"; # or "unstable" + # Use https://search.nixos.org/packages to find packages + packages = [ + pkgs.nodejs_20 + pkgs.util-linux + ]; + # Sets environment variables in the workspace + env = { + #TODO Get a API key from https://g.co/ai/idxGetGeminiKey + GOOGLE_GENAI_API_KEY = ""; + }; + idx = { + # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" + extensions = [ + ]; + + # Workspace lifecycle hooks + workspace = { + # Runs when a workspace is first created + onCreate = { + npm-install = "npm ci --no-audit --prefer-offline --no-progress --timing"; + default.openFiles = [ "README.md" "src/terminal.ts" ]; + }; + # Runs when the workspace is (re)started + onStart = { + run-server = "if [ -z \"\${GOOGLE_GENAI_API_KEY}\" ]; then \ + echo 'No Gemini API key detected, enter a Gemini API key from https://aistudio.google.com/app/apikey:' && \ + read -s GOOGLE_GENAI_API_KEY && \ + echo 'You can also set the key in .idx/dev.nix to automatically add to your workspace' + export GOOGLE_GENAI_API_KEY; \ + fi && \ + npm run genkit:dev"; + }; + }; + }; +} \ No newline at end of file diff --git a/samples/js-schoolAgent/package-lock.json b/samples/js-schoolAgent/package-lock.json index 919abbffb..277dd1113 100644 --- a/samples/js-schoolAgent/package-lock.json +++ b/samples/js-schoolAgent/package-lock.json @@ -6,19 +6,19 @@ "packages": { "": { "name": "school-agent", - "version": "1.0.0", + "version": "1.0.1", "license": "ISC", "dependencies": { - "@genkit-ai/googleai": "^1.0.0-rc.12", - "genkit": "^1.0.0-rc.12", - "google-auth-library": "^1.0.0-rc.12", + "@genkit-ai/googleai": "^1.0.0-rc.14", + "genkit": "^1.0.0-rc.14", + "google-auth-library": "^9.6.3", "llm-chunk": "^0.0.1", "pdf-parse": "^1.1.1" }, "devDependencies": { "@types/pdf-parse": "^1.1.4", "cross-env": "^7.0.3", - "genkit-cli": "^1.0.0-rc.12", + "genkit-cli": "^1.0.0-rc.14", "rimraf": "^6.0.1", "tsx": "^4.19.1", "typescript": "^5.3.3" @@ -441,11 +441,11 @@ } }, "node_modules/@genkit-ai/ai": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.12.tgz", - "integrity": "sha512-1hofDfuTEVDfryy7klgeJwCyXOp4wEZ+VPU34Dts9S5PYoN1nb23KAhnz+6dHgM42REM1PI5Zh2Wq8O10BXIww==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.14.tgz", + "integrity": "sha512-5CHyRixumiM5uHkzwHl7IWbrYSzWvhDvYAsMoq9mmI8iCJA9g/3u8JOcHSRiseuaap5gv/QGm809IpIlak55ZA==", "dependencies": { - "@genkit-ai/core": "1.0.0-rc.12", + "@genkit-ai/core": "1.0.0-rc.14", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.11.19", "colorette": "^2.0.20", @@ -457,9 +457,9 @@ } }, "node_modules/@genkit-ai/core": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.12.tgz", - "integrity": "sha512-3K7GVXR1vnJu2ANICbojxKpBoxIncuGIvn0NJ7T+s3/IOBQcusmkUyJoc4TdeRsZdX693J+oMUSx+ZE18s28eQ==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.14.tgz", + "integrity": "sha512-8h0Vl3SCP0vYIN60uELaZWEAP6BjLxXTcoWv2MFn7khxblzVkMWq98IxPThHo3s5BGB+Pxnhnsywdb/ItliFUA==", "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -482,173 +482,26 @@ } }, "node_modules/@genkit-ai/googleai": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-1.0.0-rc.12.tgz", - "integrity": "sha512-P56jclocNjgwSBizjieMc99D7WJYXAAaI7WOjkuij7oyt6BOvzCZ9rcuTJBtH/n3PRCD1P0ixt8l3m6SrxYjNQ==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-1.0.0-rc.14.tgz", + "integrity": "sha512-zeEWzPhVUpi6iJ7fHS4Hn1WnwrNLyA02LNHG6f/9iaEkCQJBbV3YHHdpRA2lIXSAwhdx619KPBoFRM2JeGm9YQ==", "dependencies": { "@google/generative-ai": "^0.21.0", "google-auth-library": "^9.6.3", "node-fetch": "^3.3.2" }, "peerDependencies": { - "genkit": "^1.0.0-rc.12" - } - }, - "node_modules/@genkit-ai/googleai/node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/@genkit-ai/googleai/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@genkit-ai/googleai/node_modules/gaxios": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", - "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", - "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "is-stream": "^2.0.0", - "node-fetch": "^2.6.9", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@genkit-ai/googleai/node_modules/gaxios/node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/@genkit-ai/googleai/node_modules/gcp-metadata": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", - "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", - "dependencies": { - "gaxios": "^6.1.1", - "google-logging-utils": "^0.0.2", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@genkit-ai/googleai/node_modules/google-auth-library": { - "version": "9.15.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", - "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", - "dependencies": { - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "gaxios": "^6.1.1", - "gcp-metadata": "^6.1.0", - "gtoken": "^7.0.0", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@genkit-ai/googleai/node_modules/gtoken": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", - "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", - "dependencies": { - "gaxios": "^6.0.0", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@genkit-ai/googleai/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@genkit-ai/googleai/node_modules/jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/@genkit-ai/googleai/node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", - "dependencies": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/@genkit-ai/googleai/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==" - }, - "node_modules/@genkit-ai/googleai/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" + "genkit": "^1.0.0-rc.14" } }, "node_modules/@genkit-ai/telemetry-server": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.12.tgz", - "integrity": "sha512-hP1qQrFkarq3+A4KQITjoGaw52MBOGKIkRiqr9HbYYXd8KOQiJ7KNo9Hv7VixphG/WQ700RUMvqjB9H4DzXZyg==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.14.tgz", + "integrity": "sha512-cFxLS8IkXhRds9+wgaru3QirgehQImG9CuhLfjo09MRcIM21Vr9F083YVnlO8WDckWz5NwclkfFKkD25FDpE/w==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", - "@genkit-ai/tools-common": "1.0.0-rc.12", + "@genkit-ai/tools-common": "1.0.0-rc.14", "@google-cloud/firestore": "^7.6.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -662,9 +515,9 @@ } }, "node_modules/@genkit-ai/tools-common": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.12.tgz", - "integrity": "sha512-N6qvS92Xt2qqnsqoQfuKveQd+4bBeRhEWlwP0e99tfmcFSfL4MDj7JLvfaoNq2GGkr4xBURQjB9NGtojZksyNg==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.14.tgz", + "integrity": "sha512-50PE/CIL3u9YC4c3vifz6CVSlC1cmQ3xAHMOsUxsRRvcPzIbL8jOCYMKAnbdgAkp0WGgWG+fLjiKbUPZydEgcg==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", @@ -1905,6 +1758,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -1953,14 +1807,11 @@ } }, "node_modules/agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dependencies": { - "es6-promisify": "^5.0.0" - }, + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "engines": { - "node": ">= 4.0.0" + "node": ">= 14" } }, "node_modules/ajv": { @@ -2072,12 +1923,14 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true }, "node_modules/axios": { "version": "1.7.9", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -2449,6 +2302,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -2608,6 +2462,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, "engines": { "node": ">=0.4.0" } @@ -2764,19 +2619,6 @@ "node": ">= 0.4" } }, - "node_modules/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "node_modules/es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", - "dependencies": { - "es6-promise": "^4.0.3" - } - }, "node_modules/esbuild": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", @@ -2865,6 +2707,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, "engines": { "node": ">=6" } @@ -3087,6 +2930,7 @@ "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, "funding": [ { "type": "individual", @@ -3122,6 +2966,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -3193,14 +3038,18 @@ "dev": true }, "node_modules/gaxios": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-1.8.4.tgz", - "integrity": "sha512-BoENMnu1Gav18HcpV9IleMPZ9exM+AvUjrAOV4Mzs/vfz2Lu/ABv451iEXByKiMPn2M140uul1txXCg83sAENw==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", "dependencies": { - "abort-controller": "^3.0.0", "extend": "^3.0.2", - "https-proxy-agent": "^2.2.1", - "node-fetch": "^2.3.0" + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" } }, "node_modules/gaxios/node_modules/node-fetch": { @@ -3222,66 +3071,49 @@ } } }, - "node_modules/gcp-metadata": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.6.3.tgz", - "integrity": "sha512-MSmczZctbz91AxCvqp9GHBoZOSbJKAICV7Ow/AIWSJZRrRchUd5NL1b2P4OfP+4m490BEUPhhARfpHdqCxuCvg==", - "dependencies": { - "axios": "^0.18.0", - "extend": "^3.0.1", - "retry-axios": "0.3.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/gcp-metadata/node_modules/axios": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", - "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", - "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410", - "dependencies": { - "follow-redirects": "1.5.10", - "is-buffer": "^2.0.2" - } - }, - "node_modules/gcp-metadata/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dependencies": { - "ms": "2.0.0" + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/gcp-metadata/node_modules/follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", "dependencies": { - "debug": "=3.1.0" + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" }, "engines": { - "node": ">=4.0" + "node": ">=14" } }, "node_modules/genkit": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.12.tgz", - "integrity": "sha512-IsL8erWfFraJGENqYf+StJakoAkfsq4VHLhQkSU5iBNYways3BumsP1KT5CKNK3lpHGJWg65LrGznAxpJ5xTqQ==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.14.tgz", + "integrity": "sha512-JB6fTcJ5CI4UhsP4uNyXpi9/OatyhPwOkBXnbHafE5rdibXuKUXadbb2LC+PWfG3+7WrLXGKlrj7kOIN3Hq2lw==", "dependencies": { - "@genkit-ai/ai": "1.0.0-rc.12", - "@genkit-ai/core": "1.0.0-rc.12", + "@genkit-ai/ai": "1.0.0-rc.14", + "@genkit-ai/core": "1.0.0-rc.14", "uuid": "^10.0.0" } }, "node_modules/genkit-cli": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.12.tgz", - "integrity": "sha512-itEg/RDIs7CW9Oho7K0sb0luERwwksxkJvAkstINCroK8s9tkoDQAPlROKeV830sMeP9kze74EG2nySsOm6fvg==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.14.tgz", + "integrity": "sha512-JR8Vvm2rGGQMsBkq+dWSypUC4ij1Uew5cjFaS74flyv/glV6RMNMuXP5XQBc5C9zIkfXeT+kng8BghHM4Gx/4g==", "dev": true, "dependencies": { - "@genkit-ai/telemetry-server": "1.0.0-rc.12", - "@genkit-ai/tools-common": "1.0.0-rc.12", + "@genkit-ai/telemetry-server": "1.0.0-rc.14", + "@genkit-ai/tools-common": "1.0.0-rc.14", "axios": "^1.7.7", "colorette": "^2.0.20", "commander": "^11.1.0", @@ -3409,49 +3241,19 @@ } }, "node_modules/google-auth-library": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-1.6.1.tgz", - "integrity": "sha512-jYiWC8NA9n9OtQM7ANn0Tk464do9yhKEtaJ72pKcaBiEwn4LwcGYIYOfwtfsSm3aur/ed3tlSxbmg24IAT6gAg==", - "dependencies": { - "axios": "^0.18.0", - "gcp-metadata": "^0.6.3", - "gtoken": "^2.3.0", - "jws": "^3.1.5", - "lodash.isstring": "^4.0.1", - "lru-cache": "^4.1.3", - "retry-axios": "^0.3.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/google-auth-library/node_modules/axios": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", - "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", - "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410", - "dependencies": { - "follow-redirects": "1.5.10", - "is-buffer": "^2.0.2" - } - }, - "node_modules/google-auth-library/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/google-auth-library/node_modules/follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", "dependencies": { - "debug": "=3.1.0" + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" }, "engines": { - "node": ">=4.0" + "node": ">=14" } }, "node_modules/google-gax": { @@ -3477,132 +3279,6 @@ "node": ">=14" } }, - "node_modules/google-gax/node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "dev": true, - "engines": { - "node": ">= 14" - } - }, - "node_modules/google-gax/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/google-gax/node_modules/gaxios": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", - "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", - "dev": true, - "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "is-stream": "^2.0.0", - "node-fetch": "^2.6.9", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/google-gax/node_modules/gcp-metadata": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", - "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", - "dev": true, - "dependencies": { - "gaxios": "^6.1.1", - "google-logging-utils": "^0.0.2", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/google-gax/node_modules/google-auth-library": { - "version": "9.15.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", - "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", - "dev": true, - "dependencies": { - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "gaxios": "^6.1.1", - "gcp-metadata": "^6.1.0", - "gtoken": "^7.0.0", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/google-gax/node_modules/gtoken": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", - "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", - "dev": true, - "dependencies": { - "gaxios": "^6.0.0", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/google-gax/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/google-gax/node_modules/jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", - "dev": true, - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/google-gax/node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", - "dev": true, - "dependencies": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/google-gax/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==", - "dev": true - }, "node_modules/google-gax/node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -3644,19 +3320,6 @@ "node": ">=14" } }, - "node_modules/google-p12-pem": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-1.0.5.tgz", - "integrity": "sha512-50rTrqYPTPPwlu9TNl/HkJbBENEpbRzTOVLFJ4YWM86njZgXHFy+FP+tLRSd9m132Li9Dqi27Z3KIWDEv5y+EA==", - "deprecated": "Package is no longer maintained", - "dependencies": { - "node-forge": "^0.10.0", - "pify": "^4.0.0" - }, - "bin": { - "gp12-pem": "build/src/bin/gp12-pem.js" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -3675,18 +3338,15 @@ "dev": true }, "node_modules/gtoken": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-2.3.3.tgz", - "integrity": "sha512-EaB49bu/TCoNeQjhCYKI/CurooBKkGxIqFHsWABW0b25fobBYVTMe84A8EBVVZhl8emiUdNypil9huMOTmyAnw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", "dependencies": { - "gaxios": "^1.0.4", - "google-p12-pem": "^1.0.0", - "jws": "^3.1.5", - "mime": "^2.2.0", - "pify": "^4.0.0" + "gaxios": "^6.0.0", + "jws": "^4.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=14.0.0" } }, "node_modules/handlebars": { @@ -3805,23 +3465,31 @@ "dev": true }, "node_modules/https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" + "agent-base": "^7.1.2", + "debug": "4" }, "engines": { - "node": ">= 4.5.0" + "node": ">= 14" } }, "node_modules/https-proxy-agent/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dependencies": { - "ms": "^2.1.1" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/https-proxy-agent/node_modules/ms": { @@ -3937,28 +3605,6 @@ "node": ">=8" } }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -4143,9 +3789,9 @@ } }, "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -4153,11 +3799,11 @@ } }, "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^2.0.0", "safe-buffer": "^5.0.1" } }, @@ -4183,11 +3829,6 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4238,13 +3879,10 @@ "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==" }, "node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true }, "node_modules/make-dir": { "version": "3.1.0", @@ -4309,14 +3947,14 @@ } }, "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "bin": { "mime": "cli.js" }, "engines": { - "node": ">=4.0.0" + "node": ">=4" } }, "node_modules/mime-db": { @@ -4448,14 +4086,6 @@ "url": "https://opencollective.com/node-fetch" } }, - "node_modules/node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4639,12 +4269,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", @@ -4702,14 +4326,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "engines": { - "node": ">=6" - } - }, "node_modules/proto3-json-serializer": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", @@ -4760,7 +4376,8 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true }, "node_modules/ps-tree": { "version": "1.2.0", @@ -4777,11 +4394,6 @@ "node": ">= 0.10" } }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" - }, "node_modules/pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -4951,14 +4563,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "node_modules/retry-axios": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/retry-axios/-/retry-axios-0.3.2.tgz", - "integrity": "sha512-jp4YlI0qyDFfXiXGhkCOliBN1G7fRH03Nqy8YdShzGqbY5/9S2x/IR6C88ls2DFkbWuL3ASkP7QD3pVrNpPgwQ==", - "peerDependencies": { - "axios": "*" - } - }, "node_modules/retry-request": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", @@ -5163,17 +4767,6 @@ "node": ">= 0.8" } }, - "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5932,11 +5525,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - }, "node_modules/yaml": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", diff --git a/samples/js-schoolAgent/package.json b/samples/js-schoolAgent/package.json index ce2d821e5..475483e9d 100644 --- a/samples/js-schoolAgent/package.json +++ b/samples/js-schoolAgent/package.json @@ -1,6 +1,6 @@ { "name": "school-agent", - "version": "1.0.0", + "version": "1.0.1", "description": "", "main": "lib/index.js", "scripts": { From 68ecd12cc780984e962bd0cf28ef2a8dae1824ae Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Mon, 3 Feb 2025 11:53:27 -0500 Subject: [PATCH 492/562] chore: update prompts to 1.0, renames to js-prompts to match other samples (#1796) --- samples/idx-template.json | 3 +- samples/js-prompts/.idx/dev.nix | 43 +++++ samples/{prompts => js-prompts}/README.md | 0 .../{prompts => js-prompts}/package-lock.json | 172 ++++++++---------- samples/{prompts => js-prompts}/package.json | 7 +- samples/{prompts => js-prompts}/src/index.ts | 11 +- samples/{prompts => js-prompts}/tsconfig.json | 0 7 files changed, 131 insertions(+), 105 deletions(-) create mode 100644 samples/js-prompts/.idx/dev.nix rename samples/{prompts => js-prompts}/README.md (100%) rename samples/{prompts => js-prompts}/package-lock.json (97%) rename samples/{prompts => js-prompts}/package.json (77%) rename samples/{prompts => js-prompts}/src/index.ts (89%) rename samples/{prompts => js-prompts}/tsconfig.json (100%) diff --git a/samples/idx-template.json b/samples/idx-template.json index 64fa3a14c..dc88e63cb 100644 --- a/samples/idx-template.json +++ b/samples/idx-template.json @@ -16,7 +16,8 @@ "options": { "js-character-generator": "Simple Character Generator", "js-coffee-shop": "Coffeeshop w Several types of Prompt", - "js-schoolAgent": "A Multi-Agent School Assistant" + "js-schoolAgent": "A Multi-Agent School Assistant", + "js-prompts": "A sample with various prompt styles" } } ] diff --git a/samples/js-prompts/.idx/dev.nix b/samples/js-prompts/.idx/dev.nix new file mode 100644 index 000000000..873d60137 --- /dev/null +++ b/samples/js-prompts/.idx/dev.nix @@ -0,0 +1,43 @@ +## Default Nix Environment for Typescript + Gemini Examples +## Requires the sample to be started with npx run genkit:dev + +# To learn more about how to use Nix to configure your environment +# see: https://developers.google.com/idx/guides/customize-idx-env +{ pkgs, ... }: { + # Which nixpkgs channel to use. + channel = "stable-24.05"; # or "unstable" + # Use https://search.nixos.org/packages to find packages + packages = [ + pkgs.nodejs_20 + pkgs.util-linux + ]; + # Sets environment variables in the workspace + env = { + #TODO Get a API key from https://g.co/ai/idxGetGeminiKey + GOOGLE_GENAI_API_KEY = ""; + }; + idx = { + # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" + extensions = [ + ]; + + # Workspace lifecycle hooks + workspace = { + # Runs when a workspace is first created + onCreate = { + npm-install = "npm ci --no-audit --prefer-offline --no-progress --timing"; + default.openFiles = [ "README.md" "src/index.ts" ]; + }; + # Runs when the workspace is (re)started + onStart = { + run-server = "if [ -z \"\${GOOGLE_GENAI_API_KEY}\" ]; then \ + echo 'No Gemini API key detected, enter a Gemini API key from https://aistudio.google.com/app/apikey:' && \ + read -s GOOGLE_GENAI_API_KEY && \ + echo 'You can also set the key in .idx/dev.nix to automatically add to your workspace' + export GOOGLE_GENAI_API_KEY; \ + fi && \ + npm run genkit:dev"; + }; + }; + }; +} \ No newline at end of file diff --git a/samples/prompts/README.md b/samples/js-prompts/README.md similarity index 100% rename from samples/prompts/README.md rename to samples/js-prompts/README.md diff --git a/samples/prompts/package-lock.json b/samples/js-prompts/package-lock.json similarity index 97% rename from samples/prompts/package-lock.json rename to samples/js-prompts/package-lock.json index 49e253a71..e6c42dd0a 100644 --- a/samples/prompts/package-lock.json +++ b/samples/js-prompts/package-lock.json @@ -9,13 +9,14 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@genkit-ai/googleai": "^0.9.0-rc || ^0.9", + "@genkit-ai/express": "^1.0.0-rc.14", + "@genkit-ai/googleai": "^1.0.0-rc.14", "express": "^4.21.0", - "genkit": "^0.9.0-rc || ^0.9", + "genkit": "^1.0.0-rc.14", "zod": "^3.23.8" }, "devDependencies": { - "genkit-cli": "^0.9.0-rc || ^0.9", + "genkit-cli": "^1.0.0-rc.14", "typescript": "^5.5.4" } }, @@ -436,14 +437,15 @@ } }, "node_modules/@genkit-ai/ai": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-0.9.12.tgz", - "integrity": "sha512-xyVVAIGKNpj5zCkoEfWZkzwctl0/hmpX6vKZgdgMH2MiqP5LzTp7rUekBMon8c1rMDVAze97QVSjAmZIoMLSlA==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.14.tgz", + "integrity": "sha512-5CHyRixumiM5uHkzwHl7IWbrYSzWvhDvYAsMoq9mmI8iCJA9g/3u8JOcHSRiseuaap5gv/QGm809IpIlak55ZA==", "dependencies": { - "@genkit-ai/core": "0.9.12", + "@genkit-ai/core": "1.0.0-rc.14", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.11.19", "colorette": "^2.0.20", + "dotprompt": "^1.0.0-dev.3 || ^1", "json5": "^2.2.3", "node-fetch": "^3.3.2", "partial-json": "^0.1.7", @@ -451,9 +453,9 @@ } }, "node_modules/@genkit-ai/core": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-0.9.12.tgz", - "integrity": "sha512-QPJZ3TL5Iq2fyeo30MpUjd3ZLcYQf97RsitDZhMbGy3vMwbgig0nhEbJ6v/qaWsOMqSfIxJE/gETY3mMts1vRg==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.14.tgz", + "integrity": "sha512-8h0Vl3SCP0vYIN60uELaZWEAP6BjLxXTcoWv2MFn7khxblzVkMWq98IxPThHo3s5BGB+Pxnhnsywdb/ItliFUA==", "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -461,11 +463,13 @@ "@opentelemetry/sdk-metrics": "^1.25.0", "@opentelemetry/sdk-node": "^0.52.0", "@opentelemetry/sdk-trace-base": "^1.25.0", + "@types/json-schema": "^7.0.15", "ajv": "^8.12.0", "ajv-formats": "^3.0.1", "async-mutex": "^0.5.0", "body-parser": "^1.20.3", "cors": "^2.8.5", + "dotprompt": "^1.0.0-dev.3 || ^1", "express": "^4.21.0", "get-port": "^5.1.0", "json-schema": "^0.4.0", @@ -473,39 +477,40 @@ "zod-to-json-schema": "^3.22.4" } }, - "node_modules/@genkit-ai/dotprompt": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/dotprompt/-/dotprompt-0.9.12.tgz", - "integrity": "sha512-eEHBRzRVemiPuqCBbXiLgltNWpmCHmC+gVHBhsAnrfOYBlwmPvh2nnAPBXGYnkDH87PuN11jg8YJQmO4kQuoSw==", + "node_modules/@genkit-ai/express": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/express/-/express-1.0.0-rc.14.tgz", + "integrity": "sha512-I8VcuV2iyJXs/X3nCQZ/UR+FIO7XwaBo6+KWSDnugJrCweQZQ0LCYHbinSp1obLm/xCML7CWnqpZTX87mnbe3w==", "dependencies": { - "@genkit-ai/ai": "0.9.12", - "@genkit-ai/core": "0.9.12", - "front-matter": "^4.0.2", - "handlebars": "^4.7.8", - "node-fetch": "^3.3.2" + "body-parser": "^1.20.3", + "cors": "^2.8.5" + }, + "peerDependencies": { + "express": "^4.21.1", + "genkit": "^1.0.0-rc.14" } }, "node_modules/@genkit-ai/googleai": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-0.9.12.tgz", - "integrity": "sha512-q6bX9Nq4xVpzH4vd9W+rg4xO7SVodG516K4BVM04DtxU5w0ogwLl/mRPEU0VIoISjD6FwaC4sPjPkEerTp8a4g==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-1.0.0-rc.14.tgz", + "integrity": "sha512-zeEWzPhVUpi6iJ7fHS4Hn1WnwrNLyA02LNHG6f/9iaEkCQJBbV3YHHdpRA2lIXSAwhdx619KPBoFRM2JeGm9YQ==", "dependencies": { "@google/generative-ai": "^0.21.0", "google-auth-library": "^9.6.3", "node-fetch": "^3.3.2" }, "peerDependencies": { - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.14" } }, "node_modules/@genkit-ai/telemetry-server": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-0.9.12.tgz", - "integrity": "sha512-8w1O9LOUtMCGc8bx+Vcd5D68L8ctM6H544Vxnfvy8gx4JHV5dj7vQiI9YDgm65pgP15pytQzt1TcRtOtYoimFw==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.14.tgz", + "integrity": "sha512-cFxLS8IkXhRds9+wgaru3QirgehQImG9CuhLfjo09MRcIM21Vr9F083YVnlO8WDckWz5NwclkfFKkD25FDpE/w==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", - "@genkit-ai/tools-common": "0.9.12", + "@genkit-ai/tools-common": "1.0.0-rc.14", "@google-cloud/firestore": "^7.6.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -519,14 +524,16 @@ } }, "node_modules/@genkit-ai/tools-common": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-0.9.12.tgz", - "integrity": "sha512-A9ToM/CY6vxcBSw4o47Q7qrpMaRsk4P6ZuzoDMlCzCVI+RZN5ulTL4LXMrq6nQ/KxY1GT94bSoulROXBMzW/Gw==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.14.tgz", + "integrity": "sha512-50PE/CIL3u9YC4c3vifz6CVSlC1cmQ3xAHMOsUxsRRvcPzIbL8jOCYMKAnbdgAkp0WGgWG+fLjiKbUPZydEgcg==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", "@trpc/server": "10.45.0", "adm-zip": "^0.5.12", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", "axios": "^1.7.7", "body-parser": "^1.20.2", "chokidar": "^3.5.3", @@ -549,24 +556,6 @@ "zod-to-json-schema": "^3.22.4" } }, - "node_modules/@genkit-ai/tools-common/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==", - "dev": true - }, - "node_modules/@genkit-ai/tools-common/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@genkit-ai/tools-common/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -1695,6 +1684,11 @@ "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, "node_modules/@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", @@ -1905,12 +1899,10 @@ } }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/array-flatten": { "version": "1.1.1", @@ -2168,9 +2160,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==" + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==" }, "node_modules/cli-cursor": { "version": "3.1.0", @@ -2498,6 +2490,15 @@ "node": ">=8" } }, + "node_modules/dotprompt": { + "version": "1.0.0-dev.3", + "resolved": "https://registry.npmjs.org/dotprompt/-/dotprompt-1.0.0-dev.3.tgz", + "integrity": "sha512-gAcIhG+vzZlrZcBN/lO8gEMUtXHKMLbs1+snJuZrPkZnNgnWrzz1hYN4vYotpng/5fHsYSPUEckECy6qUNSrsQ==", + "dependencies": { + "handlebars": "^4.7.8", + "yaml": "^2.5.0" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2664,18 +2665,6 @@ "node": ">=0.8.0" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -3005,14 +2994,6 @@ "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", "dev": true }, - "node_modules/front-matter": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", - "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", - "dependencies": { - "js-yaml": "^3.13.1" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3100,24 +3081,23 @@ } }, "node_modules/genkit": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/genkit/-/genkit-0.9.12.tgz", - "integrity": "sha512-m1VQE/yhuii0y1aGTnkoSesSXTNE25q1s7vv5YVgJWa/t2gOXuznZOoHTJ847f/3mKC7fgnV7xGI+t/+7wbe0g==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.14.tgz", + "integrity": "sha512-JB6fTcJ5CI4UhsP4uNyXpi9/OatyhPwOkBXnbHafE5rdibXuKUXadbb2LC+PWfG3+7WrLXGKlrj7kOIN3Hq2lw==", "dependencies": { - "@genkit-ai/ai": "0.9.12", - "@genkit-ai/core": "0.9.12", - "@genkit-ai/dotprompt": "0.9.12", + "@genkit-ai/ai": "1.0.0-rc.14", + "@genkit-ai/core": "1.0.0-rc.14", "uuid": "^10.0.0" } }, "node_modules/genkit-cli": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-0.9.12.tgz", - "integrity": "sha512-Qsk7TmVBxghTT+0cRe2j0fYJboUIfWhuUoeVbWshranye6tConGMyDEZdXBIeMWIixSbG7k8a7a2INdRdjcQJw==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.14.tgz", + "integrity": "sha512-JR8Vvm2rGGQMsBkq+dWSypUC4ij1Uew5cjFaS74flyv/glV6RMNMuXP5XQBc5C9zIkfXeT+kng8BghHM4Gx/4g==", "dev": true, "dependencies": { - "@genkit-ai/telemetry-server": "0.9.12", - "@genkit-ai/tools-common": "0.9.12", + "@genkit-ai/telemetry-server": "1.0.0-rc.14", + "@genkit-ai/tools-common": "1.0.0-rc.14", "axios": "^1.7.7", "colorette": "^2.0.20", "commander": "^11.1.0", @@ -3731,12 +3711,12 @@ } }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -4790,11 +4770,6 @@ "node": "*" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -5398,7 +5373,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", - "dev": true, "bin": { "yaml": "bin.mjs" }, diff --git a/samples/prompts/package.json b/samples/js-prompts/package.json similarity index 77% rename from samples/prompts/package.json rename to samples/js-prompts/package.json index 081931c36..af227f43d 100644 --- a/samples/prompts/package.json +++ b/samples/js-prompts/package.json @@ -14,13 +14,14 @@ "author": "", "license": "ISC", "dependencies": { - "genkit": "^0.9.0-rc || ^0.9", - "@genkit-ai/googleai": "^0.9.0-rc || ^0.9", + "genkit": "^1.0.0-rc.14", + "@genkit-ai/googleai": "^1.0.0-rc.14", + "@genkit-ai/express": "^1.0.0-rc.14", "express": "^4.21.0", "zod": "^3.23.8" }, "devDependencies": { - "genkit-cli": "^0.9.0-rc || ^0.9", + "genkit-cli": "^1.0.0-rc.14", "typescript": "^5.5.4" } } diff --git a/samples/prompts/src/index.ts b/samples/js-prompts/src/index.ts similarity index 89% rename from samples/prompts/src/index.ts rename to samples/js-prompts/src/index.ts index 9149c0358..5241dd1a6 100644 --- a/samples/prompts/src/index.ts +++ b/samples/js-prompts/src/index.ts @@ -15,6 +15,7 @@ */ // Import the Genkit core libraries and plugins. +import { startFlowServer } from '@genkit-ai/express'; import { googleAI } from '@genkit-ai/googleai'; import { genkit, z } from 'genkit'; @@ -77,13 +78,19 @@ const threeGreetingsPrompt = ai.definePrompt( const threeGreetings = ai.defineFlow('threeGreetingsPrompt', async () => { const response = await threeGreetingsPrompt({ name: 'Fred' }); - return response.output?.likeAPirate; + return response.output; }); // Start a flow server, which exposes your flows as HTTP endpoints. This call // must come last, after all of your plug-in configuration and flow definitions. // You can optionally specify a subset of flows to serve, and configure some // HTTP server options, but by default, the flow server serves all defined flows. -ai.startFlowServer({ +startFlowServer({ flows: [threeGreetings, simpleTemplate, simpleDotprompt, simplePrompt], }); + +// Call one of the flows just to validate everything is hooked up properly +helloDotprompt({ name: 'Bob' }).then((generateResponse) => { + console.log('\nThe model response:'); + console.log(generateResponse.text); +}); diff --git a/samples/prompts/tsconfig.json b/samples/js-prompts/tsconfig.json similarity index 100% rename from samples/prompts/tsconfig.json rename to samples/js-prompts/tsconfig.json From 4bae15e28011cc5151401325420862f7231a1694 Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Mon, 3 Feb 2025 11:54:30 -0500 Subject: [PATCH 493/562] fix: update API ref link (#1795) --- docs/evaluation.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/evaluation.md b/docs/evaluation.md index efac99152..385a6da04 100644 --- a/docs/evaluation.md +++ b/docs/evaluation.md @@ -234,8 +234,7 @@ and is only enforced if a schema is specified on the target flow. control for advanced use cases (e.g. providing model parameters, message history, tools, etc). You can find the full schema for `GenerateRequest` in our [API reference - docs](https://genkit-js-api.web.app/interfaces/genkit._.GenerateRequest.html). - + docs](https://js.api.genkit.dev/interfaces/genkit._.GenerateRequest.html). Note: Schema validation is a helper tool for editing examples, but it is possible to save an example with invalid schema. These examples may fail when From da81888a832db703bd5ce19c26407e8a44a65f39 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Mon, 3 Feb 2025 09:12:04 -0800 Subject: [PATCH 494/562] chore(js): Adds Registry#apiStability for fine-grained control of beta features. (#1749) --- js/ai/src/generate.ts | 2 ++ js/ai/src/tool.ts | 35 ++++++++++++++++++++++++++++------- js/ai/tests/tool_test.ts | 22 +++++++++++++--------- js/core/src/error.ts | 27 +++++++++++++++++++++++++++ js/core/src/index.ts | 2 +- js/core/src/registry.ts | 1 + js/genkit/src/beta.ts | 23 +++++++++++++++++++++++ 7 files changed, 95 insertions(+), 17 deletions(-) diff --git a/js/ai/src/generate.ts b/js/ai/src/generate.ts index 825b5d5ef..8e84d0a3c 100755 --- a/js/ai/src/generate.ts +++ b/js/ai/src/generate.ts @@ -131,6 +131,8 @@ export interface GenerateOptions< * resume: myInterrupt.respond(interrupt, {note: "this is the reply data"}), * }); * ``` + * + * @beta */ resume?: ResumeOptions; /** When true, return tool calls for manual processing instead of automatically resolving them. */ diff --git a/js/ai/src/tool.ts b/js/ai/src/tool.ts index e618c9bda..cf00ef83e 100644 --- a/js/ai/src/tool.ts +++ b/js/ai/src/tool.ts @@ -18,6 +18,7 @@ import { Action, ActionContext, ActionRunOptions, + assertUnstable, defineAction, JSONSchema7, stripUndefinedProps, @@ -50,6 +51,8 @@ export type ToolAction< * respond constructs a tool response corresponding to the provided interrupt tool request * using the provided reply data, validating it against the output schema of the tool if * it exists. + * + * @beta */ respond( /** The interrupt tool request to which you want to respond. */ @@ -71,6 +74,8 @@ export type ToolAction< * @param interrupt The interrupt tool request you want to restart. * @param resumedMetadata The metadata you want to provide to the tool to aide in reprocessing. Defaults to `true` if none is supplied. * @param options Additional options for restarting the tool. + * + * @beta */ restart( interrupt: ToolRequestPart, @@ -87,7 +92,10 @@ export type ToolAction< }; export interface ToolRunOptions extends ActionRunOptions { - /** If resumed is supplied to a tool at runtime, that means that it was previously interrupted and this is a second */ + /** + * If resumed is supplied to a tool at runtime, that means that it was previously interrupted and this is a second + * @beta + **/ resumed?: boolean | Record; /** The metadata from the tool request that triggered this run. */ metadata?: Record; @@ -260,12 +268,17 @@ export function defineTool( return fn(i, { ...runOptions, context: { ...runOptions.context }, - interrupt: interruptTool, + interrupt: interruptTool(registry), }); } ); - (a as ToolAction).respond = (interrupt, replyData, options) => { - parseSchema(replyData, { + (a as ToolAction).respond = (interrupt, responseData, options) => { + assertUnstable( + registry, + 'beta', + "The 'tool.reply' method is part of the 'interrupts' beta feature." + ); + parseSchema(responseData, { jsonSchema: config.outputJsonSchema, schema: config.outputSchema, }); @@ -273,7 +286,7 @@ export function defineTool( toolResponse: stripUndefinedProps({ name: interrupt.toolRequest.name, ref: interrupt.toolRequest.ref, - output: replyData, + output: responseData, }), metadata: { interruptResponse: options?.metadata || true, @@ -282,6 +295,11 @@ export function defineTool( }; (a as ToolAction).restart = (interrupt, resumedMetadata, options) => { + assertUnstable( + registry, + 'beta', + "The 'tool.restart' method is part of the 'interrupts' beta feature." + ); let replaceInput = options?.replaceInput; if (replaceInput) { replaceInput = parseSchema(replaceInput, { @@ -359,6 +377,9 @@ export class ToolInterruptError extends Error { * Interrupts current tool execution causing tool request to be returned in the generation response. * Should only be called within a tool. */ -function interruptTool(metadata?: Record): never { - throw new ToolInterruptError(metadata); +function interruptTool(registry: Registry) { + return (metadata?: Record): never => { + assertUnstable(registry, 'beta', 'Tool interrupts are a beta feature.'); + throw new ToolInterruptError(metadata); + }; } diff --git a/js/ai/tests/tool_test.ts b/js/ai/tests/tool_test.ts index 5da178792..1cdf0f50e 100644 --- a/js/ai/tests/tool_test.ts +++ b/js/ai/tests/tool_test.ts @@ -22,32 +22,34 @@ import { defineInterrupt, defineTool } from '../src/tool.js'; describe('defineInterrupt', () => { let registry = new Registry(); + registry.apiStability = 'beta'; afterEach(() => { registry = new Registry(); + registry.apiStability = 'beta'; }); function expectInterrupt(fn: () => any, metadata?: Record) { - assert.rejects(fn, { name: 'ToolInterruptError', metadata }); + return assert.rejects(fn, { name: 'ToolInterruptError', metadata }); } - it('should throw a simple interrupt with no metadata', () => { + it('should throw a simple interrupt with no metadata', async () => { const simple = defineInterrupt(registry, { name: 'simple', description: 'simple interrupt', }); - expectInterrupt(async () => { + await expectInterrupt(async () => { await simple({}); }); }); - it('should throw a simple interrupt with fixed metadata', () => { + it('should throw a simple interrupt with fixed metadata', async () => { const simple = defineInterrupt(registry, { name: 'simple', description: 'simple interrupt', requestMetadata: { foo: 'bar' }, }); - expectInterrupt( + await expectInterrupt( async () => { await simple({}); }, @@ -55,14 +57,14 @@ describe('defineInterrupt', () => { ); }); - it('should throw a simple interrupt with function-returned metadata', () => { + it('should throw a simple interrupt with function-returned metadata', async () => { const simple = defineInterrupt(registry, { name: 'simple', description: 'simple interrupt', inputSchema: z.string(), requestMetadata: (foo) => ({ foo }), }); - expectInterrupt( + await expectInterrupt( async () => { await simple('bar'); }, @@ -70,14 +72,14 @@ describe('defineInterrupt', () => { ); }); - it('should throw a simple interrupt with async function-returned metadata', () => { + it('should throw a simple interrupt with async function-returned metadata', async () => { const simple = defineInterrupt(registry, { name: 'simple', description: 'simple interrupt', inputSchema: z.string(), requestMetadata: async (foo) => ({ foo }), }); - expectInterrupt( + await expectInterrupt( async () => { await simple('bar'); }, @@ -106,8 +108,10 @@ describe('defineInterrupt', () => { describe('defineTool', () => { let registry = new Registry(); + registry.apiStability = 'beta'; afterEach(() => { registry = new Registry(); + registry.apiStability = 'beta'; }); describe('.respond()', () => { diff --git a/js/core/src/error.ts b/js/core/src/error.ts index b5fdaa574..2a5e24c91 100644 --- a/js/core/src/error.ts +++ b/js/core/src/error.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Registry } from './registry.js'; import { StatusName } from './statusTypes.js'; /** @@ -42,6 +43,32 @@ export class GenkitError extends Error { } } +export class UnstableApiError extends GenkitError { + constructor(level: 'beta', message?: string) { + super({ + status: 'FAILED_PRECONDITION', + message: `${message ? message + ' ' : ''}This API requires '${level}' stability level.\n\nTo use this feature, initialize Genkit using \`import {genkit} from "genkit/${level}"\`.`, + }); + this.name = 'UnstableApiError'; + } +} + +/** + * assertUnstable allows features to raise exceptions when using Genkit from *more* stable initialized instances. + * + * @param level The maximum stability channel allowed. + * @param message An optional message describing which feature is not allowed. + */ +export function assertUnstable( + registry: Registry, + level: 'beta', + message?: string +) { + if (level === 'beta' && registry.apiStability === 'stable') { + throw new UnstableApiError(level, message); + } +} + /** * Extracts error message from the given error object, or if input is not an error then just turn the error into a string. */ diff --git a/js/core/src/index.ts b/js/core/src/index.ts index 3c3ea2e8e..7f438b789 100644 --- a/js/core/src/index.ts +++ b/js/core/src/index.ts @@ -30,7 +30,7 @@ export const GENKIT_REFLECTION_API_SPEC_VERSION = 1; export { z } from 'zod'; export * from './action.js'; export { getContext, runWithContext, type ActionContext } from './context.js'; -export { GenkitError } from './error.js'; +export { GenkitError, UnstableApiError, assertUnstable } from './error.js'; export { defineFlow, run, diff --git a/js/core/src/registry.ts b/js/core/src/registry.ts index af9888de3..457022bb3 100644 --- a/js/core/src/registry.ts +++ b/js/core/src/registry.ts @@ -73,6 +73,7 @@ export class Registry { private schemasByName: Record = {}; private valueByTypeAndName: Record> = {}; private allPluginsInitialized = false; + public apiStability: 'stable' | 'beta' = 'stable'; readonly asyncStore = new AsyncStore(); readonly dotprompt = new Dotprompt({ diff --git a/js/genkit/src/beta.ts b/js/genkit/src/beta.ts index f36c21e65..96af62885 100644 --- a/js/genkit/src/beta.ts +++ b/js/genkit/src/beta.ts @@ -42,6 +42,8 @@ import { Genkit, GenkitOptions } from './genkit'; * * This will create a new Genkit registry, register the provided plugins, stores, and other configuration. This * should be called before any flows are registered. + * + * @beta */ export function genkit(options: GenkitOptions): GenkitBeta { return new GenkitBeta(options); @@ -49,8 +51,15 @@ export function genkit(options: GenkitOptions): GenkitBeta { /** * Genkit BETA APIs. + * + * @beta */ export class GenkitBeta extends Genkit { + constructor(options?: GenkitOptions) { + super(options); + this.registry.apiStability = 'beta'; + } + /** * Create a chat session with the provided options. * @@ -61,6 +70,8 @@ export class GenkitBeta extends Genkit { * let response = await chat.send('tell me a joke') * response = await chat.send('another one') * ``` + * + * @beta */ chat(options?: ChatOptions): Chat; @@ -74,6 +85,8 @@ export class GenkitBeta extends Genkit { * const chat = ai.chat(triageAgent) * const { text } = await chat.send('my phone feels hot'); * ``` + * + * @beta */ chat(preamble: ExecutablePrompt, options?: ChatOptions): Chat; @@ -87,6 +100,8 @@ export class GenkitBeta extends Genkit { * let response = await chat.send('tell me a joke') * response = await chat.send('another one') * ``` + * + * @beta */ chat( preambleOrOptions?: ChatOptions | ExecutablePrompt, @@ -130,6 +145,8 @@ export class GenkitBeta extends Genkit { /** * Loads a session from the store. + * + * @beta */ async loadSession( sessionId: string, @@ -149,6 +166,8 @@ export class GenkitBeta extends Genkit { /** * Gets the current session from async local storage. + * + * @beta */ currentSession(): Session { const currentSession = getCurrentSession(this.registry); @@ -190,6 +209,8 @@ export class GenkitBeta extends Genkit { * output: { format: 'customJson', schema: MenuItemSchema }, * }); * ``` + * + * @beta */ defineFormat( options: { @@ -205,6 +226,8 @@ export class GenkitBeta extends Genkit { * * Interrupts are special tools that halt model processing and return control back to the caller. Interrupts make it simpler to implement * "human-in-the-loop" and out-of-band processing patterns that require waiting on external actions to complete. + * + * @beta */ defineInterrupt( config: InterruptConfig From 0d69f25a130ca578ddadb83c8ec69e602ca10ebb Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Mon, 3 Feb 2025 12:32:19 -0500 Subject: [PATCH 495/562] fix: remove idx subbutton and lint fixes (#1794) * fix: remove idx subbutton * fix: remove idx button on sample and fix some linter complaints --- samples/js-character-generator/README.md | 2 ++ samples/js-character-generator/package.json | 2 +- samples/js-coffee-shop/README.md | 19 +------------------ samples/js-menu/README.md | 6 +++--- 4 files changed, 7 insertions(+), 22 deletions(-) diff --git a/samples/js-character-generator/README.md b/samples/js-character-generator/README.md index 2f67f5068..c1de6d8fe 100644 --- a/samples/js-character-generator/README.md +++ b/samples/js-character-generator/README.md @@ -1,5 +1,6 @@ # Character Generator + + This sample is used as the Genkit template in Project IDX, you can check it out in IDX! diff --git a/samples/js-character-generator/package.json b/samples/js-character-generator/package.json index 79ce42513..5ebbcfce0 100644 --- a/samples/js-character-generator/package.json +++ b/samples/js-character-generator/package.json @@ -1,5 +1,5 @@ { - "name": "Character Generator", + "name": "character_generator", "version": "0.1.0", "description": "A simple Firebase Genkit app to generate fantasy characters", "main": "index.js", diff --git a/samples/js-coffee-shop/README.md b/samples/js-coffee-shop/README.md index d5de16b93..0378ec103 100644 --- a/samples/js-coffee-shop/README.md +++ b/samples/js-coffee-shop/README.md @@ -1,24 +1,7 @@ -## Running the sample +# Running the sample ```bash export GOOGLE_GENAI_API_KEY= npm i npm run genkit:dev ``` - -or try it out on Project IDX, our Cloud-based IDE - - - - - - Try in IDX - - diff --git a/samples/js-menu/README.md b/samples/js-menu/README.md index 8200d81b6..982ea26a8 100644 --- a/samples/js-menu/README.md +++ b/samples/js-menu/README.md @@ -1,4 +1,4 @@ -## Menu Understanding Sample Application +# Menu Understanding Sample Application This sample demonstrates an application that can understand a restaurant menu and answer relevant questions about the items on the menu. @@ -6,11 +6,11 @@ There are 5 iterations of this sample application, growing in complexity and dem To test each one out, open the Developer UI and exercise the prompts and flows. Each step contains one or more `example.json` files which you can use as inputs. -### Prerequisites +## Prerequisites This example uses Vertex AI for language models and embeddings. -### Prompts and Flows +## Prompts and Flows 1. This step shows how to define prompts in code that can accept user input to their templates. 2. This step illustrates how to wrap your llm calls and other application code into flows with strong input and output schemas. From 04dd55ca1c77b561afc9e22e68696278381e51c6 Mon Sep 17 00:00:00 2001 From: Yesudeep Mangalapilly Date: Mon, 3 Feb 2025 10:15:09 -0800 Subject: [PATCH 496/562] chore: use spdx-license-identifier for license #1736 (#1777) --- go/ai/document.go | 14 ++------- go/ai/document_test.go | 14 ++------- go/ai/embedder.go | 14 ++------- go/ai/gen.go | 14 ++------- go/ai/generate.go | 14 ++------- go/ai/generator_test.go | 14 ++------- go/ai/prompt.go | 14 ++------- go/ai/request_helpers.go | 14 ++------- go/ai/retriever.go | 14 ++------- go/ai/tools.go | 14 ++------- go/core/action.go | 14 ++------- go/core/action_test.go | 14 ++------- go/core/core.go | 14 ++------- go/core/file_flow_state_store.go | 14 ++------- go/core/flow_state_store.go | 14 ++------- go/core/logger/logger.go | 14 ++------- go/core/tracing/milliseconds.go | 14 ++------- go/core/tracing/milliseconds_test.go | 14 ++------- go/core/tracing/store.go | 14 ++------- go/core/tracing/store_test.go | 14 ++------- go/core/tracing/telemetry.go | 14 ++------- go/core/tracing/trace_server_exporter.go | 14 ++------- go/core/tracing/trace_server_exporter_test.go | 14 ++------- go/core/tracing/tracing.go | 14 ++------- go/core/tracing/tracing_test.go | 14 ++------- go/genkit/conformance_test.go | 14 ++------- go/genkit/flow.go | 14 ++------- go/genkit/flow_test.go | 14 ++------- go/genkit/gen.go | 14 ++------- go/genkit/genkit.go | 14 ++------- go/genkit/genkit_test.go | 14 ++------- go/genkit/servers.go | 14 ++------- go/genkit/servers_test.go | 14 ++------- go/internal/action/action.go | 14 ++------- go/internal/atype/atype.go | 14 ++------- go/internal/base/context_key.go | 14 ++------- go/internal/base/json.go | 14 ++------- go/internal/base/json_test.go | 14 ++------- go/internal/base/misc.go | 14 ++------- go/internal/base/validation.go | 14 ++------- go/internal/cmd/copy/copy.go | 14 ++------- go/internal/cmd/copy/copy_test.go | 14 ++------- go/internal/cmd/jsonschemagen/jsonschema.go | 14 ++------- .../cmd/jsonschemagen/jsonschema_test.go | 14 ++------- .../cmd/jsonschemagen/jsonschemagen.go | 30 ++++--------------- .../cmd/jsonschemagen/jsonschemagen_test.go | 14 ++------- go/internal/cmd/jsonschemagen/testdata/golden | 15 ++-------- go/internal/cmd/weave/weave.go | 14 ++------- go/internal/doc-snippets/doc.go | 14 ++------- go/internal/doc-snippets/dotprompt.go | 14 ++------- go/internal/doc-snippets/flows.go | 14 ++------- go/internal/doc-snippets/gcp.go | 14 ++------- go/internal/doc-snippets/googleai.go | 14 ++------- go/internal/doc-snippets/init/main.go | 14 ++------- .../doc-snippets/modelplugin/modelplugin.go | 14 ++------- go/internal/doc-snippets/models.go | 14 ++------- go/internal/doc-snippets/ollama.go | 14 ++------- go/internal/doc-snippets/pinecone.go | 14 ++------- go/internal/doc-snippets/plugin/plugin.go | 14 ++------- go/internal/doc-snippets/prompts.go | 14 ++------- go/internal/doc-snippets/rag/main.go | 14 ++------- go/internal/doc-snippets/rag/pdf/pdf.go | 14 ++------- .../rag/textsplitter/textsplitter.go | 14 ++------- .../doc-snippets/telemetryplugin/exporters.go | 14 ++------- .../telemetryplugin/telemetryplugin.go | 14 ++------- go/internal/doc-snippets/vertexai.go | 14 ++------- go/internal/fakeembedder/fakeembedder.go | 14 ++------- go/internal/fakeembedder/fakeembedder_test.go | 14 ++------- go/internal/metrics/metrics.go | 14 ++------- go/internal/registry/registry.go | 14 ++------- go/internal/version.go | 14 ++------- go/plugins/dotprompt/dotprompt.go | 14 ++------- go/plugins/dotprompt/dotprompt_test.go | 14 ++------- go/plugins/dotprompt/genkit.go | 14 ++------- go/plugins/dotprompt/genkit_test.go | 14 ++------- go/plugins/dotprompt/picoschema.go | 14 ++------- go/plugins/dotprompt/picoschema_test.go | 14 ++------- go/plugins/dotprompt/render.go | 14 ++------- go/plugins/dotprompt/render_test.go | 14 ++------- go/plugins/firebase/auth.go | 14 ++------- go/plugins/firebase/auth_test.go | 14 ++------- go/plugins/firebase/firebase.go | 14 ++------- go/plugins/googleai/googleai.go | 14 ++------- go/plugins/googleai/googleai_test.go | 14 ++------- go/plugins/googlecloud/googlecloud.go | 14 ++------- go/plugins/googlecloud/googlecloud_test.go | 14 ++------- go/plugins/googlecloud/slog_handler.go | 14 ++------- go/plugins/googlecloud/slog_handler_test.go | 14 ++------- go/plugins/internal/gemini/gemini.go | 14 ++------- go/plugins/internal/uri/uri.go | 14 ++------- go/plugins/internal/uri/uri_test.go | 14 ++------- go/plugins/localvec/localvec.go | 14 ++------- go/plugins/localvec/localvec_test.go | 14 ++------- go/plugins/ollama/embed.go | 14 ++------- go/plugins/ollama/embed_test.go | 14 ++------- go/plugins/ollama/ollama.go | 14 ++------- go/plugins/ollama/ollama_live_test.go | 14 ++------- go/plugins/ollama/ollama_test.go | 14 ++------- go/plugins/pinecone/genkit.go | 14 ++------- go/plugins/pinecone/genkit_test.go | 14 ++------- go/plugins/pinecone/pinecone.go | 14 ++------- go/plugins/pinecone/pinecone_test.go | 14 ++------- go/plugins/vertexai/embed.go | 14 ++------- go/plugins/vertexai/vertexai.go | 14 ++------- go/plugins/vertexai/vertexai_test.go | 14 ++------- go/plugins/weaviate/weaviate.go | 14 ++------- go/plugins/weaviate/weaviate_test.go | 14 ++------- go/samples/coffee-shop/main.go | 14 ++------- go/samples/firebase-auth/main.go | 14 ++------- go/samples/flow-sample1/main.go | 14 ++------- go/samples/menu/main.go | 14 ++------- go/samples/menu/s01.go | 14 ++------- go/samples/menu/s02.go | 14 ++------- go/samples/menu/s03.go | 14 ++------- go/samples/menu/s04.go | 14 ++------- go/samples/menu/s05.go | 14 ++------- go/samples/pgvector/main.go | 14 ++------- go/samples/rag/main.go | 14 ++------- go/tests/api_test.go | 14 ++------- go/tests/test_app/main.go | 14 ++------- go/tests/utils/utils.go | 14 ++------- py/bin/build_dists | 13 +------- py/bin/check-licenses | 3 ++ py/bin/cleanup | 3 ++ py/bin/fmt | 15 ++-------- py/bin/format_toml_files | 13 +------- py/bin/run_tests | 3 ++ py/bin/setup | 16 ++-------- .../dotprompt/src/dotprompt/__init__.py | 13 -------- py/packages/genkit/src/genkit/ai/__init__.py | 13 -------- py/packages/genkit/src/genkit/ai/model.py | 13 -------- py/packages/genkit/src/genkit/ai/prompt.py | 13 -------- .../genkit/src/genkit/core/__init__.py | 13 -------- py/packages/genkit/src/genkit/core/action.py | 13 -------- .../genkit/src/genkit/core/reflection.py | 13 -------- .../genkit/src/genkit/core/registry.py | 13 -------- py/packages/genkit/src/genkit/core/tracing.py | 13 -------- py/packages/genkit/src/genkit/core/types.py | 13 -------- .../genkit/src/genkit/veneer/__init__.py | 13 -------- .../handlebarz/src/handlebarz/__init__.py | 13 -------- .../src/genkit/plugins/chroma/__init__.py | 13 -------- .../src/genkit/plugins/firebase/__init__.py | 13 -------- .../src/genkit/plugins/google_ai/__init__.py | 13 -------- .../plugins/google_ai/models/__init__.py | 13 -------- .../genkit/plugins/google_cloud/__init__.py | 13 -------- .../src/genkit/plugins/ollama/__init__.py | 13 -------- .../genkit/plugins/ollama/models/__init__.py | 13 -------- .../src/genkit/plugins/pinecone/__init__.py | 13 -------- .../src/genkit/plugins/vertex_ai/__init__.py | 13 -------- .../plugins/vertex_ai/models/__init__.py | 13 -------- py/samples/hello/hello.py | 13 -------- py/tests/smoke/package_test.py | 13 -------- 152 files changed, 260 insertions(+), 1829 deletions(-) diff --git a/go/ai/document.go b/go/ai/document.go index e2dec4580..da753cf6d 100644 --- a/go/ai/document.go +++ b/go/ai/document.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ai diff --git a/go/ai/document_test.go b/go/ai/document_test.go index 49345c4a4..da67e6101 100644 --- a/go/ai/document_test.go +++ b/go/ai/document_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ai diff --git a/go/ai/embedder.go b/go/ai/embedder.go index da3dae507..322a4548d 100644 --- a/go/ai/embedder.go +++ b/go/ai/embedder.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ai diff --git a/go/ai/gen.go b/go/ai/gen.go index 409e4f096..a674ddac7 100644 --- a/go/ai/gen.go +++ b/go/ai/gen.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // This file was generated by jsonschemagen. DO NOT EDIT. diff --git a/go/ai/generate.go b/go/ai/generate.go index 5a02b03ca..a577751af 100644 --- a/go/ai/generate.go +++ b/go/ai/generate.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ai diff --git a/go/ai/generator_test.go b/go/ai/generator_test.go index 0c5796b92..b82bda62d 100644 --- a/go/ai/generator_test.go +++ b/go/ai/generator_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ai diff --git a/go/ai/prompt.go b/go/ai/prompt.go index 7643997e3..b36cdc8ff 100644 --- a/go/ai/prompt.go +++ b/go/ai/prompt.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ai diff --git a/go/ai/request_helpers.go b/go/ai/request_helpers.go index 3632e5c6a..2c51e33da 100644 --- a/go/ai/request_helpers.go +++ b/go/ai/request_helpers.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ai diff --git a/go/ai/retriever.go b/go/ai/retriever.go index a239eb755..141cecdb1 100644 --- a/go/ai/retriever.go +++ b/go/ai/retriever.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ai diff --git a/go/ai/tools.go b/go/ai/tools.go index 2cae9db65..6519cd1ab 100644 --- a/go/ai/tools.go +++ b/go/ai/tools.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ai diff --git a/go/core/action.go b/go/core/action.go index b6900f27e..abb14ed3d 100644 --- a/go/core/action.go +++ b/go/core/action.go @@ -1,16 +1,6 @@ // Copyright 2024 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 core diff --git a/go/core/action_test.go b/go/core/action_test.go index bdb64db0c..694ff7609 100644 --- a/go/core/action_test.go +++ b/go/core/action_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 core diff --git a/go/core/core.go b/go/core/core.go index 5895b8e92..f74f6a26b 100644 --- a/go/core/core.go +++ b/go/core/core.go @@ -1,16 +1,6 @@ // Copyright 2024 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 npm script that generates JSON Schemas from the zod types // in the *.ts files. It writes the result to genkit-tools/genkit-schema.json diff --git a/go/core/file_flow_state_store.go b/go/core/file_flow_state_store.go index d01993cd3..132860ed6 100644 --- a/go/core/file_flow_state_store.go +++ b/go/core/file_flow_state_store.go @@ -1,16 +1,6 @@ // Copyright 2024 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 core diff --git a/go/core/flow_state_store.go b/go/core/flow_state_store.go index b5a2abc4c..055c93e80 100644 --- a/go/core/flow_state_store.go +++ b/go/core/flow_state_store.go @@ -1,16 +1,6 @@ // Copyright 2024 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 core diff --git a/go/core/logger/logger.go b/go/core/logger/logger.go index 11f37014e..9092dc430 100644 --- a/go/core/logger/logger.go +++ b/go/core/logger/logger.go @@ -1,16 +1,6 @@ // Copyright 2024 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 logger provides a context-scoped slog.Logger. package logger diff --git a/go/core/tracing/milliseconds.go b/go/core/tracing/milliseconds.go index 79315cf13..a7a3addc9 100644 --- a/go/core/tracing/milliseconds.go +++ b/go/core/tracing/milliseconds.go @@ -1,16 +1,6 @@ // Copyright 2024 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 gtime provides time functionality for Go Genkit. package tracing diff --git a/go/core/tracing/milliseconds_test.go b/go/core/tracing/milliseconds_test.go index 49694ef3f..cb02ee075 100644 --- a/go/core/tracing/milliseconds_test.go +++ b/go/core/tracing/milliseconds_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 tracing diff --git a/go/core/tracing/store.go b/go/core/tracing/store.go index fbbab092b..c0505e111 100644 --- a/go/core/tracing/store.go +++ b/go/core/tracing/store.go @@ -1,16 +1,6 @@ // Copyright 2024 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 tracing diff --git a/go/core/tracing/store_test.go b/go/core/tracing/store_test.go index 44ab23296..856048352 100644 --- a/go/core/tracing/store_test.go +++ b/go/core/tracing/store_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 tracing diff --git a/go/core/tracing/telemetry.go b/go/core/tracing/telemetry.go index 6025a54a2..15c3bad0c 100644 --- a/go/core/tracing/telemetry.go +++ b/go/core/tracing/telemetry.go @@ -1,16 +1,6 @@ // Copyright 2024 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 tracing diff --git a/go/core/tracing/trace_server_exporter.go b/go/core/tracing/trace_server_exporter.go index e33e4a504..820354182 100644 --- a/go/core/tracing/trace_server_exporter.go +++ b/go/core/tracing/trace_server_exporter.go @@ -1,16 +1,6 @@ // Copyright 2024 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 tracing diff --git a/go/core/tracing/trace_server_exporter_test.go b/go/core/tracing/trace_server_exporter_test.go index 487b00f92..d5301b82e 100644 --- a/go/core/tracing/trace_server_exporter_test.go +++ b/go/core/tracing/trace_server_exporter_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 tracing diff --git a/go/core/tracing/tracing.go b/go/core/tracing/tracing.go index cae0bf161..874960e61 100644 --- a/go/core/tracing/tracing.go +++ b/go/core/tracing/tracing.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // The tracing package provides support for execution traces. package tracing diff --git a/go/core/tracing/tracing_test.go b/go/core/tracing/tracing_test.go index 87577b195..c8ccfa100 100644 --- a/go/core/tracing/tracing_test.go +++ b/go/core/tracing/tracing_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 tracing diff --git a/go/genkit/conformance_test.go b/go/genkit/conformance_test.go index c7c1c3c5a..42e7dc550 100644 --- a/go/genkit/conformance_test.go +++ b/go/genkit/conformance_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 genkit diff --git a/go/genkit/flow.go b/go/genkit/flow.go index 52ede66c8..b0a5d39fc 100644 --- a/go/genkit/flow.go +++ b/go/genkit/flow.go @@ -1,16 +1,6 @@ // Copyright 2024 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 genkit diff --git a/go/genkit/flow_test.go b/go/genkit/flow_test.go index 589918229..9fa573a61 100644 --- a/go/genkit/flow_test.go +++ b/go/genkit/flow_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 genkit diff --git a/go/genkit/gen.go b/go/genkit/gen.go index 325b101ee..8168e18f2 100644 --- a/go/genkit/gen.go +++ b/go/genkit/gen.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // This file was generated by jsonschemagen. DO NOT EDIT. diff --git a/go/genkit/genkit.go b/go/genkit/genkit.go index 122edd376..68c5147e7 100644 --- a/go/genkit/genkit.go +++ b/go/genkit/genkit.go @@ -1,16 +1,6 @@ // Copyright 2024 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 genkit provides Genkit functionality for application developers. package genkit diff --git a/go/genkit/genkit_test.go b/go/genkit/genkit_test.go index 104ab2fe1..db4fdfb33 100644 --- a/go/genkit/genkit_test.go +++ b/go/genkit/genkit_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 genkit diff --git a/go/genkit/servers.go b/go/genkit/servers.go index 9dd175328..c094654ce 100644 --- a/go/genkit/servers.go +++ b/go/genkit/servers.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // This file implements production and development servers. // diff --git a/go/genkit/servers_test.go b/go/genkit/servers_test.go index 09378a931..6ee102013 100644 --- a/go/genkit/servers_test.go +++ b/go/genkit/servers_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 genkit diff --git a/go/internal/action/action.go b/go/internal/action/action.go index ddd0119b5..0600a4a9d 100644 --- a/go/internal/action/action.go +++ b/go/internal/action/action.go @@ -1,16 +1,6 @@ // Copyright 2024 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 action diff --git a/go/internal/atype/atype.go b/go/internal/atype/atype.go index f5b48c207..da4f54d50 100644 --- a/go/internal/atype/atype.go +++ b/go/internal/atype/atype.go @@ -1,16 +1,6 @@ // Copyright 2024 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 atype provides types for Genkit actions. package atype diff --git a/go/internal/base/context_key.go b/go/internal/base/context_key.go index 38f476f77..b976f4832 100644 --- a/go/internal/base/context_key.go +++ b/go/internal/base/context_key.go @@ -1,16 +1,6 @@ // Copyright 2024 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 base diff --git a/go/internal/base/json.go b/go/internal/base/json.go index 168f34eff..ce3561060 100644 --- a/go/internal/base/json.go +++ b/go/internal/base/json.go @@ -1,16 +1,6 @@ // Copyright 2024 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 base diff --git a/go/internal/base/json_test.go b/go/internal/base/json_test.go index 6387acb50..5b9ae9393 100644 --- a/go/internal/base/json_test.go +++ b/go/internal/base/json_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 base diff --git a/go/internal/base/misc.go b/go/internal/base/misc.go index 01b0fa840..ef2290791 100644 --- a/go/internal/base/misc.go +++ b/go/internal/base/misc.go @@ -1,16 +1,6 @@ // Copyright 2024 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 base diff --git a/go/internal/base/validation.go b/go/internal/base/validation.go index 9510fd68e..cd24bedc8 100644 --- a/go/internal/base/validation.go +++ b/go/internal/base/validation.go @@ -1,16 +1,6 @@ // Copyright 2024 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 base diff --git a/go/internal/cmd/copy/copy.go b/go/internal/cmd/copy/copy.go index f6192a320..6c7bf7531 100644 --- a/go/internal/cmd/copy/copy.go +++ b/go/internal/cmd/copy/copy.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // copy is a tool for copying parts of files. // It reads a set of source files, collecting named sequences of lines to copy diff --git a/go/internal/cmd/copy/copy_test.go b/go/internal/cmd/copy/copy_test.go index 24cabaa0f..241cd0555 100644 --- a/go/internal/cmd/copy/copy_test.go +++ b/go/internal/cmd/copy/copy_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 main diff --git a/go/internal/cmd/jsonschemagen/jsonschema.go b/go/internal/cmd/jsonschemagen/jsonschema.go index 183c5e4cf..e5e50ecb4 100644 --- a/go/internal/cmd/jsonschemagen/jsonschema.go +++ b/go/internal/cmd/jsonschemagen/jsonschema.go @@ -1,16 +1,6 @@ // Copyright 2024 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 main diff --git a/go/internal/cmd/jsonschemagen/jsonschema_test.go b/go/internal/cmd/jsonschemagen/jsonschema_test.go index 1fb4c5ec7..f00060ba9 100644 --- a/go/internal/cmd/jsonschemagen/jsonschema_test.go +++ b/go/internal/cmd/jsonschemagen/jsonschema_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 main diff --git a/go/internal/cmd/jsonschemagen/jsonschemagen.go b/go/internal/cmd/jsonschemagen/jsonschemagen.go index d5dd2cb0b..8056990af 100644 --- a/go/internal/cmd/jsonschemagen/jsonschemagen.go +++ b/go/internal/cmd/jsonschemagen/jsonschemagen.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // A simple, self-contained code generator for JSON Schema. // It converts a JSON Schema to equivalent Go types. @@ -233,19 +223,9 @@ func nameAnonymousTypes(schemas map[string]*Schema) { } const license = ` -// Copyright 2024 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. +// Copyright 2025 Google LLC +// SPDX-License-Identifier: Apache-2.0 + ` type generator struct { diff --git a/go/internal/cmd/jsonschemagen/jsonschemagen_test.go b/go/internal/cmd/jsonschemagen/jsonschemagen_test.go index 7a0dabe4d..0f976ad7f 100644 --- a/go/internal/cmd/jsonschemagen/jsonschemagen_test.go +++ b/go/internal/cmd/jsonschemagen/jsonschemagen_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 main diff --git a/go/internal/cmd/jsonschemagen/testdata/golden b/go/internal/cmd/jsonschemagen/testdata/golden index 349125698..90469d145 100644 --- a/go/internal/cmd/jsonschemagen/testdata/golden +++ b/go/internal/cmd/jsonschemagen/testdata/golden @@ -1,16 +1,5 @@ -// Copyright 2024 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. +// Copyright 2025 Google LLC +// SPDX-License-Identifier: Apache-2.0 // This file was generated by jsonschemagen. DO NOT EDIT. diff --git a/go/internal/cmd/weave/weave.go b/go/internal/cmd/weave/weave.go index 9f69560e2..1911d37e4 100644 --- a/go/internal/cmd/weave/weave.go +++ b/go/internal/cmd/weave/weave.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // The weave command is a simple preprocessor for markdown files. // It builds a table of contents and processes %include directives. diff --git a/go/internal/doc-snippets/doc.go b/go/internal/doc-snippets/doc.go index 9e9f9d1b9..b59152e60 100644 --- a/go/internal/doc-snippets/doc.go +++ b/go/internal/doc-snippets/doc.go @@ -1,16 +1,6 @@ // Copyright 2024 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 snippets contains snippets that are incorporated into // documentation (see the docs-go directory under the repo root). diff --git a/go/internal/doc-snippets/dotprompt.go b/go/internal/doc-snippets/dotprompt.go index 8c6cff48f..c374e5931 100644 --- a/go/internal/doc-snippets/dotprompt.go +++ b/go/internal/doc-snippets/dotprompt.go @@ -1,16 +1,6 @@ // Copyright 2024 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 snippets diff --git a/go/internal/doc-snippets/flows.go b/go/internal/doc-snippets/flows.go index cfc45894f..98e32e469 100644 --- a/go/internal/doc-snippets/flows.go +++ b/go/internal/doc-snippets/flows.go @@ -1,16 +1,6 @@ // Copyright 2024 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 snippets diff --git a/go/internal/doc-snippets/gcp.go b/go/internal/doc-snippets/gcp.go index 1efee223c..44f7c0a64 100644 --- a/go/internal/doc-snippets/gcp.go +++ b/go/internal/doc-snippets/gcp.go @@ -1,16 +1,6 @@ // Copyright 2024 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 snippets diff --git a/go/internal/doc-snippets/googleai.go b/go/internal/doc-snippets/googleai.go index ef9cee755..1f9c45d03 100644 --- a/go/internal/doc-snippets/googleai.go +++ b/go/internal/doc-snippets/googleai.go @@ -1,16 +1,6 @@ // Copyright 2024 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 snippets diff --git a/go/internal/doc-snippets/init/main.go b/go/internal/doc-snippets/init/main.go index b68cf377e..44d9c3bc7 100644 --- a/go/internal/doc-snippets/init/main.go +++ b/go/internal/doc-snippets/init/main.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // [START main] package main diff --git a/go/internal/doc-snippets/modelplugin/modelplugin.go b/go/internal/doc-snippets/modelplugin/modelplugin.go index cb14c02eb..657651674 100644 --- a/go/internal/doc-snippets/modelplugin/modelplugin.go +++ b/go/internal/doc-snippets/modelplugin/modelplugin.go @@ -1,16 +1,6 @@ // Copyright 2024 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 modelplugin diff --git a/go/internal/doc-snippets/models.go b/go/internal/doc-snippets/models.go index 97709c89c..6639afa17 100644 --- a/go/internal/doc-snippets/models.go +++ b/go/internal/doc-snippets/models.go @@ -1,16 +1,6 @@ // Copyright 2024 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 snippets diff --git a/go/internal/doc-snippets/ollama.go b/go/internal/doc-snippets/ollama.go index 76afebc02..8665137a7 100644 --- a/go/internal/doc-snippets/ollama.go +++ b/go/internal/doc-snippets/ollama.go @@ -1,16 +1,6 @@ // Copyright 2024 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 snippets diff --git a/go/internal/doc-snippets/pinecone.go b/go/internal/doc-snippets/pinecone.go index 9995c93c2..2de2c07e8 100644 --- a/go/internal/doc-snippets/pinecone.go +++ b/go/internal/doc-snippets/pinecone.go @@ -1,16 +1,6 @@ // Copyright 2024 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 snippets diff --git a/go/internal/doc-snippets/plugin/plugin.go b/go/internal/doc-snippets/plugin/plugin.go index 7a0fdbe7b..209e0604b 100644 --- a/go/internal/doc-snippets/plugin/plugin.go +++ b/go/internal/doc-snippets/plugin/plugin.go @@ -1,16 +1,6 @@ // Copyright 2024 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 plugin diff --git a/go/internal/doc-snippets/prompts.go b/go/internal/doc-snippets/prompts.go index c8bb0e3a3..7944f977c 100644 --- a/go/internal/doc-snippets/prompts.go +++ b/go/internal/doc-snippets/prompts.go @@ -1,16 +1,6 @@ // Copyright 2024 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 snippets diff --git a/go/internal/doc-snippets/rag/main.go b/go/internal/doc-snippets/rag/main.go index 638100571..aac27c025 100644 --- a/go/internal/doc-snippets/rag/main.go +++ b/go/internal/doc-snippets/rag/main.go @@ -1,16 +1,6 @@ // Copyright 2024 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 rag diff --git a/go/internal/doc-snippets/rag/pdf/pdf.go b/go/internal/doc-snippets/rag/pdf/pdf.go index 3cb2c48d8..2f34c2d5d 100644 --- a/go/internal/doc-snippets/rag/pdf/pdf.go +++ b/go/internal/doc-snippets/rag/pdf/pdf.go @@ -1,16 +1,6 @@ // Copyright 2024 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 pdf diff --git a/go/internal/doc-snippets/rag/textsplitter/textsplitter.go b/go/internal/doc-snippets/rag/textsplitter/textsplitter.go index c827cd2e8..198592fb0 100644 --- a/go/internal/doc-snippets/rag/textsplitter/textsplitter.go +++ b/go/internal/doc-snippets/rag/textsplitter/textsplitter.go @@ -1,16 +1,6 @@ // Copyright 2024 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 textsplitter diff --git a/go/internal/doc-snippets/telemetryplugin/exporters.go b/go/internal/doc-snippets/telemetryplugin/exporters.go index d0b0fca4b..c0acd042c 100644 --- a/go/internal/doc-snippets/telemetryplugin/exporters.go +++ b/go/internal/doc-snippets/telemetryplugin/exporters.go @@ -1,16 +1,6 @@ // Copyright 2024 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 telemetryplugin diff --git a/go/internal/doc-snippets/telemetryplugin/telemetryplugin.go b/go/internal/doc-snippets/telemetryplugin/telemetryplugin.go index 855ca7cc4..d230eaf66 100644 --- a/go/internal/doc-snippets/telemetryplugin/telemetryplugin.go +++ b/go/internal/doc-snippets/telemetryplugin/telemetryplugin.go @@ -1,16 +1,6 @@ // Copyright 2024 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 telemetryplugin diff --git a/go/internal/doc-snippets/vertexai.go b/go/internal/doc-snippets/vertexai.go index d09f412e4..d98c8314c 100644 --- a/go/internal/doc-snippets/vertexai.go +++ b/go/internal/doc-snippets/vertexai.go @@ -1,16 +1,6 @@ // Copyright 2024 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 snippets diff --git a/go/internal/fakeembedder/fakeembedder.go b/go/internal/fakeembedder/fakeembedder.go index 11fd2e259..316f003c2 100644 --- a/go/internal/fakeembedder/fakeembedder.go +++ b/go/internal/fakeembedder/fakeembedder.go @@ -1,16 +1,6 @@ // Copyright 2024 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 fakeembedder provides a fake implementation of // genkit.Embedder for testing purposes. diff --git a/go/internal/fakeembedder/fakeembedder_test.go b/go/internal/fakeembedder/fakeembedder_test.go index c01d037d4..ae1fc5d8a 100644 --- a/go/internal/fakeembedder/fakeembedder_test.go +++ b/go/internal/fakeembedder/fakeembedder_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 fakeembedder diff --git a/go/internal/metrics/metrics.go b/go/internal/metrics/metrics.go index a37a547b3..c51c9f93e 100644 --- a/go/internal/metrics/metrics.go +++ b/go/internal/metrics/metrics.go @@ -1,16 +1,6 @@ // Copyright 2024 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 metrics diff --git a/go/internal/registry/registry.go b/go/internal/registry/registry.go index 804898a0f..7077e7ae4 100644 --- a/go/internal/registry/registry.go +++ b/go/internal/registry/registry.go @@ -1,16 +1,6 @@ // Copyright 2024 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 registry diff --git a/go/internal/version.go b/go/internal/version.go index d1cc79ca5..5474bbd58 100644 --- a/go/internal/version.go +++ b/go/internal/version.go @@ -1,16 +1,6 @@ // Copyright 2024 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 internal diff --git a/go/plugins/dotprompt/dotprompt.go b/go/plugins/dotprompt/dotprompt.go index e667aa58d..d4e8e5f87 100644 --- a/go/plugins/dotprompt/dotprompt.go +++ b/go/plugins/dotprompt/dotprompt.go @@ -1,16 +1,6 @@ // Copyright 2024 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 dotprompt parses and renders dotprompt files. package dotprompt diff --git a/go/plugins/dotprompt/dotprompt_test.go b/go/plugins/dotprompt/dotprompt_test.go index f177409da..ee4590e4b 100644 --- a/go/plugins/dotprompt/dotprompt_test.go +++ b/go/plugins/dotprompt/dotprompt_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 dotprompt diff --git a/go/plugins/dotprompt/genkit.go b/go/plugins/dotprompt/genkit.go index 520878934..cbedefe8d 100644 --- a/go/plugins/dotprompt/genkit.go +++ b/go/plugins/dotprompt/genkit.go @@ -1,16 +1,6 @@ // Copyright 2024 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 dotprompt diff --git a/go/plugins/dotprompt/genkit_test.go b/go/plugins/dotprompt/genkit_test.go index 840e2e871..f6aa2488b 100644 --- a/go/plugins/dotprompt/genkit_test.go +++ b/go/plugins/dotprompt/genkit_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 dotprompt diff --git a/go/plugins/dotprompt/picoschema.go b/go/plugins/dotprompt/picoschema.go index ca5b7c00c..da759f765 100644 --- a/go/plugins/dotprompt/picoschema.go +++ b/go/plugins/dotprompt/picoschema.go @@ -1,16 +1,6 @@ // Copyright 2024 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 dotprompt diff --git a/go/plugins/dotprompt/picoschema_test.go b/go/plugins/dotprompt/picoschema_test.go index f25d5f6b1..8ed8f192f 100644 --- a/go/plugins/dotprompt/picoschema_test.go +++ b/go/plugins/dotprompt/picoschema_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 dotprompt diff --git a/go/plugins/dotprompt/render.go b/go/plugins/dotprompt/render.go index 98e712e91..24e3ad05d 100644 --- a/go/plugins/dotprompt/render.go +++ b/go/plugins/dotprompt/render.go @@ -1,16 +1,6 @@ // Copyright 2024 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 dotprompt diff --git a/go/plugins/dotprompt/render_test.go b/go/plugins/dotprompt/render_test.go index f85b9c259..dd6fa354d 100644 --- a/go/plugins/dotprompt/render_test.go +++ b/go/plugins/dotprompt/render_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 dotprompt diff --git a/go/plugins/firebase/auth.go b/go/plugins/firebase/auth.go index de27795a6..1e1226ebe 100644 --- a/go/plugins/firebase/auth.go +++ b/go/plugins/firebase/auth.go @@ -1,16 +1,6 @@ // Copyright 2024 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 firebase diff --git a/go/plugins/firebase/auth_test.go b/go/plugins/firebase/auth_test.go index 6bd566064..bfe24178a 100644 --- a/go/plugins/firebase/auth_test.go +++ b/go/plugins/firebase/auth_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 firebase diff --git a/go/plugins/firebase/firebase.go b/go/plugins/firebase/firebase.go index e2f4a85b4..bade89f8f 100644 --- a/go/plugins/firebase/firebase.go +++ b/go/plugins/firebase/firebase.go @@ -1,16 +1,6 @@ // Copyright 2024 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 firebase diff --git a/go/plugins/googleai/googleai.go b/go/plugins/googleai/googleai.go index f0e98fc50..8a51ce56e 100644 --- a/go/plugins/googleai/googleai.go +++ b/go/plugins/googleai/googleai.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // Parts of this file are copied into vertexai, because the code is identical // except for the import path of the Gemini SDK. diff --git a/go/plugins/googleai/googleai_test.go b/go/plugins/googleai/googleai_test.go index 5ba1e12dc..59199ed3f 100644 --- a/go/plugins/googleai/googleai_test.go +++ b/go/plugins/googleai/googleai_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 googleai_test diff --git a/go/plugins/googlecloud/googlecloud.go b/go/plugins/googlecloud/googlecloud.go index a2ccadc8c..1a8d89739 100644 --- a/go/plugins/googlecloud/googlecloud.go +++ b/go/plugins/googlecloud/googlecloud.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // The googlecloud package supports telemetry (tracing, metrics and logging) using // Google Cloud services. diff --git a/go/plugins/googlecloud/googlecloud_test.go b/go/plugins/googlecloud/googlecloud_test.go index 22a46b28b..6990f37a6 100644 --- a/go/plugins/googlecloud/googlecloud_test.go +++ b/go/plugins/googlecloud/googlecloud_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // The googlecloud package supports telemetry (tracing , metrics and logging) using // Google Cloud services. diff --git a/go/plugins/googlecloud/slog_handler.go b/go/plugins/googlecloud/slog_handler.go index 7c644b0cd..244840643 100644 --- a/go/plugins/googlecloud/slog_handler.go +++ b/go/plugins/googlecloud/slog_handler.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // The googlecloud package supports telemetry (tracing, metrics and logging) using // Google Cloud services. diff --git a/go/plugins/googlecloud/slog_handler_test.go b/go/plugins/googlecloud/slog_handler_test.go index 7feb3eb01..1e7fbb35c 100644 --- a/go/plugins/googlecloud/slog_handler_test.go +++ b/go/plugins/googlecloud/slog_handler_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // The googlecloud package supports telemetry (tracing, metrics and logging) using // Google Cloud services. diff --git a/go/plugins/internal/gemini/gemini.go b/go/plugins/internal/gemini/gemini.go index 9ede1b8f0..578b41643 100644 --- a/go/plugins/internal/gemini/gemini.go +++ b/go/plugins/internal/gemini/gemini.go @@ -1,16 +1,6 @@ // Copyright 2024 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 gemini contains code that is common to both the googleai and vertexai plugins. // Most most cannot be shared in this way because the import paths are different. diff --git a/go/plugins/internal/uri/uri.go b/go/plugins/internal/uri/uri.go index 788e7d346..914b14283 100644 --- a/go/plugins/internal/uri/uri.go +++ b/go/plugins/internal/uri/uri.go @@ -1,16 +1,6 @@ // Copyright 2024 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 uri extracts the content-type and data from a media part. // This is used by the googleai and vertexai plugins. diff --git a/go/plugins/internal/uri/uri_test.go b/go/plugins/internal/uri/uri_test.go index 8151f9a7a..d008e65e6 100644 --- a/go/plugins/internal/uri/uri_test.go +++ b/go/plugins/internal/uri/uri_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 uri diff --git a/go/plugins/localvec/localvec.go b/go/plugins/localvec/localvec.go index f0858f892..357316498 100644 --- a/go/plugins/localvec/localvec.go +++ b/go/plugins/localvec/localvec.go @@ -1,16 +1,6 @@ // Copyright 2024 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 localvec is a local vector database for development and testing. // The database is stored in a file in the local file system. diff --git a/go/plugins/localvec/localvec_test.go b/go/plugins/localvec/localvec_test.go index a479d27fe..be080de09 100644 --- a/go/plugins/localvec/localvec_test.go +++ b/go/plugins/localvec/localvec_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 localvec diff --git a/go/plugins/ollama/embed.go b/go/plugins/ollama/embed.go index b6d3cf2ef..3433cb938 100644 --- a/go/plugins/ollama/embed.go +++ b/go/plugins/ollama/embed.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ollama diff --git a/go/plugins/ollama/embed_test.go b/go/plugins/ollama/embed_test.go index d5c0190a4..61e56c6f3 100644 --- a/go/plugins/ollama/embed_test.go +++ b/go/plugins/ollama/embed_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ollama diff --git a/go/plugins/ollama/ollama.go b/go/plugins/ollama/ollama.go index 7d1bad962..3b3b51624 100644 --- a/go/plugins/ollama/ollama.go +++ b/go/plugins/ollama/ollama.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ollama diff --git a/go/plugins/ollama/ollama_live_test.go b/go/plugins/ollama/ollama_live_test.go index ccce9c537..4b4bb5c9d 100644 --- a/go/plugins/ollama/ollama_live_test.go +++ b/go/plugins/ollama/ollama_live_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ollama_test diff --git a/go/plugins/ollama/ollama_test.go b/go/plugins/ollama/ollama_test.go index a3cc591e6..559248f9c 100644 --- a/go/plugins/ollama/ollama_test.go +++ b/go/plugins/ollama/ollama_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 ollama diff --git a/go/plugins/pinecone/genkit.go b/go/plugins/pinecone/genkit.go index ab946b3c4..677b7690e 100644 --- a/go/plugins/pinecone/genkit.go +++ b/go/plugins/pinecone/genkit.go @@ -1,16 +1,6 @@ // Copyright 2024 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 pinecone diff --git a/go/plugins/pinecone/genkit_test.go b/go/plugins/pinecone/genkit_test.go index d032dc281..00d5a0f8b 100644 --- a/go/plugins/pinecone/genkit_test.go +++ b/go/plugins/pinecone/genkit_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 pinecone diff --git a/go/plugins/pinecone/pinecone.go b/go/plugins/pinecone/pinecone.go index e5a68f4e1..4e6418bd3 100644 --- a/go/plugins/pinecone/pinecone.go +++ b/go/plugins/pinecone/pinecone.go @@ -1,16 +1,6 @@ // Copyright 2024 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 pinecone implements a genkit plugin for the Pinecone vector // database. This defines an indexer and a retriever. diff --git a/go/plugins/pinecone/pinecone_test.go b/go/plugins/pinecone/pinecone_test.go index 5f5f8fdc3..30e7461c4 100644 --- a/go/plugins/pinecone/pinecone_test.go +++ b/go/plugins/pinecone/pinecone_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 pinecone diff --git a/go/plugins/vertexai/embed.go b/go/plugins/vertexai/embed.go index ae5bd5bed..529652ea2 100644 --- a/go/plugins/vertexai/embed.go +++ b/go/plugins/vertexai/embed.go @@ -1,16 +1,6 @@ // Copyright 2024 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 vertexai diff --git a/go/plugins/vertexai/vertexai.go b/go/plugins/vertexai/vertexai.go index 3cb95e2c4..49101f03d 100644 --- a/go/plugins/vertexai/vertexai.go +++ b/go/plugins/vertexai/vertexai.go @@ -1,16 +1,6 @@ // Copyright 2024 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 vertexai diff --git a/go/plugins/vertexai/vertexai_test.go b/go/plugins/vertexai/vertexai_test.go index fd322ee9e..bd9de850a 100644 --- a/go/plugins/vertexai/vertexai_test.go +++ b/go/plugins/vertexai/vertexai_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 vertexai_test diff --git a/go/plugins/weaviate/weaviate.go b/go/plugins/weaviate/weaviate.go index 40df346ea..d042d7741 100644 --- a/go/plugins/weaviate/weaviate.go +++ b/go/plugins/weaviate/weaviate.go @@ -1,16 +1,6 @@ // Copyright 2024 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 weaviate diff --git a/go/plugins/weaviate/weaviate_test.go b/go/plugins/weaviate/weaviate_test.go index c1f1ad533..46da356d1 100644 --- a/go/plugins/weaviate/weaviate_test.go +++ b/go/plugins/weaviate/weaviate_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 weaviate diff --git a/go/samples/coffee-shop/main.go b/go/samples/coffee-shop/main.go index 28e76ccf6..74ac6cd56 100755 --- a/go/samples/coffee-shop/main.go +++ b/go/samples/coffee-shop/main.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // This program can be manually tested like so: // diff --git a/go/samples/firebase-auth/main.go b/go/samples/firebase-auth/main.go index 3b22dfb97..bd9af13f8 100644 --- a/go/samples/firebase-auth/main.go +++ b/go/samples/firebase-auth/main.go @@ -1,16 +1,6 @@ // Copyright 2024 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 main diff --git a/go/samples/flow-sample1/main.go b/go/samples/flow-sample1/main.go index 1906889a7..327b8aa72 100644 --- a/go/samples/flow-sample1/main.go +++ b/go/samples/flow-sample1/main.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // This program can be manually tested like so: // Start the server listening on port 3100: diff --git a/go/samples/menu/main.go b/go/samples/menu/main.go index 369e93d64..11eab3ba1 100644 --- a/go/samples/menu/main.go +++ b/go/samples/menu/main.go @@ -1,16 +1,6 @@ // Copyright 2024 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 main diff --git a/go/samples/menu/s01.go b/go/samples/menu/s01.go index 565c3cbc7..96d331b03 100644 --- a/go/samples/menu/s01.go +++ b/go/samples/menu/s01.go @@ -1,16 +1,6 @@ // Copyright 2024 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 main diff --git a/go/samples/menu/s02.go b/go/samples/menu/s02.go index 7c404bf62..6ce943531 100644 --- a/go/samples/menu/s02.go +++ b/go/samples/menu/s02.go @@ -1,16 +1,6 @@ // Copyright 2024 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 main diff --git a/go/samples/menu/s03.go b/go/samples/menu/s03.go index a2ed32d7b..7bf8bbde8 100644 --- a/go/samples/menu/s03.go +++ b/go/samples/menu/s03.go @@ -1,16 +1,6 @@ // Copyright 2024 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 main diff --git a/go/samples/menu/s04.go b/go/samples/menu/s04.go index a5c1e50b3..1c12b91ce 100644 --- a/go/samples/menu/s04.go +++ b/go/samples/menu/s04.go @@ -1,16 +1,6 @@ // Copyright 2024 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 main diff --git a/go/samples/menu/s05.go b/go/samples/menu/s05.go index 7ae82a5bd..526f83a9b 100644 --- a/go/samples/menu/s05.go +++ b/go/samples/menu/s05.go @@ -1,16 +1,6 @@ // Copyright 2024 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 main diff --git a/go/samples/pgvector/main.go b/go/samples/pgvector/main.go index aedee9a99..08a05a74d 100644 --- a/go/samples/pgvector/main.go +++ b/go/samples/pgvector/main.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // This program shows how to use Postgres's pgvector extension with Genkit. diff --git a/go/samples/rag/main.go b/go/samples/rag/main.go index fedcda02e..8fa0addb9 100644 --- a/go/samples/rag/main.go +++ b/go/samples/rag/main.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // This program can be manually tested like so: // diff --git a/go/tests/api_test.go b/go/tests/api_test.go index b4def2945..59905d24f 100644 --- a/go/tests/api_test.go +++ b/go/tests/api_test.go @@ -1,16 +1,6 @@ // Copyright 2024 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 api_test diff --git a/go/tests/test_app/main.go b/go/tests/test_app/main.go index 78821235b..55d13a7f9 100644 --- a/go/tests/test_app/main.go +++ b/go/tests/test_app/main.go @@ -1,16 +1,6 @@ // Copyright 2024 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 + // This program doesn't do anything interesting. // It is used by go/tests/api_test.go. diff --git a/go/tests/utils/utils.go b/go/tests/utils/utils.go index a6d3d172e..66f676583 100644 --- a/go/tests/utils/utils.go +++ b/go/tests/utils/utils.go @@ -1,16 +1,6 @@ // Copyright 2024 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 test_utils diff --git a/py/bin/build_dists b/py/bin/build_dists index f0a3c2274..504136680 100755 --- a/py/bin/build_dists +++ b/py/bin/build_dists @@ -3,18 +3,7 @@ # Build distributions for all projects # # 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 set -euo pipefail diff --git a/py/bin/check-licenses b/py/bin/check-licenses index f1d5516d6..0379b5a95 100755 --- a/py/bin/check-licenses +++ b/py/bin/check-licenses @@ -1,4 +1,7 @@ #!/usr/bin/env bash +# +# Copyright 2025 Google LLC +# SPDX-License-Identifier: Apache-2.0 set -euo pipefail diff --git a/py/bin/cleanup b/py/bin/cleanup index 19dfbf248..0cc1ac247 100755 --- a/py/bin/cleanup +++ b/py/bin/cleanup @@ -1,4 +1,7 @@ #!/usr/bin/env bash +# +# Copyright 2025 Google LLC +# SPDX-License-Identifier: Apache-2.0 set -x set -euo pipefail diff --git a/py/bin/fmt b/py/bin/fmt index c05600141..fda0432d2 100755 --- a/py/bin/fmt +++ b/py/bin/fmt @@ -3,18 +3,7 @@ # Format all files in the project # # 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 #set -x # Uncomment to enable tracing. set -euo pipefail @@ -28,7 +17,7 @@ TOP_DIR=$(git rev-parse --show-toplevel) addlicense \ -c "Google LLC" \ - -s \ + -s=only \ -ignore '**/.github/**/*' \ -ignore '**/.mypy_cache/**/*' \ -ignore '**/bazel-*/**/*' \ diff --git a/py/bin/format_toml_files b/py/bin/format_toml_files index 3b5ed705c..eef13cf5f 100755 --- a/py/bin/format_toml_files +++ b/py/bin/format_toml_files @@ -3,18 +3,7 @@ # Format all TOML files in the project. # # 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 set -euo pipefail diff --git a/py/bin/run_tests b/py/bin/run_tests index ffb64b9e1..7fe1339e7 100755 --- a/py/bin/run_tests +++ b/py/bin/run_tests @@ -1,6 +1,9 @@ #!/usr/bin/env bash # # Run tests for all supported Python versions +# +# Copyright 2025 Google LLC +# SPDX-License-Identifier: Apache-2.0 set -euo pipefail diff --git a/py/bin/setup b/py/bin/setup index dfb173e5f..ab2047e6a 100755 --- a/py/bin/setup +++ b/py/bin/setup @@ -3,19 +3,6 @@ # Setup script for Genkit engineering. # # 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 if ((EUID == 0)) && [[ -z ${DANGEROUSLY_RUN_AS_ROOT+x} ]]; then @@ -189,7 +176,8 @@ function genkit::install_pnpm_cli_tools() { # Biome: https://biomejs.dev/ pnpm add -g \ @biomejs/biome \ - genkit-cli + genkit-cli \ + prettier } # Install all the Python-related formatter and static analysis tools. diff --git a/py/packages/dotprompt/src/dotprompt/__init__.py b/py/packages/dotprompt/src/dotprompt/__init__.py index f1ab80247..e3d229182 100644 --- a/py/packages/dotprompt/src/dotprompt/__init__.py +++ b/py/packages/dotprompt/src/dotprompt/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/packages/genkit/src/genkit/ai/__init__.py b/py/packages/genkit/src/genkit/ai/__init__.py index 3ec7b0ed9..c0494838a 100644 --- a/py/packages/genkit/src/genkit/ai/__init__.py +++ b/py/packages/genkit/src/genkit/ai/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/packages/genkit/src/genkit/ai/model.py b/py/packages/genkit/src/genkit/ai/model.py index d0a0a9234..bb20c68c1 100644 --- a/py/packages/genkit/src/genkit/ai/model.py +++ b/py/packages/genkit/src/genkit/ai/model.py @@ -1,17 +1,4 @@ # 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 from typing import Callable diff --git a/py/packages/genkit/src/genkit/ai/prompt.py b/py/packages/genkit/src/genkit/ai/prompt.py index a32e10939..0b06c8181 100644 --- a/py/packages/genkit/src/genkit/ai/prompt.py +++ b/py/packages/genkit/src/genkit/ai/prompt.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/packages/genkit/src/genkit/core/__init__.py b/py/packages/genkit/src/genkit/core/__init__.py index 6fe59e083..2a74de4b3 100644 --- a/py/packages/genkit/src/genkit/core/__init__.py +++ b/py/packages/genkit/src/genkit/core/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/packages/genkit/src/genkit/core/action.py b/py/packages/genkit/src/genkit/core/action.py index e11a5d996..d594f9569 100644 --- a/py/packages/genkit/src/genkit/core/action.py +++ b/py/packages/genkit/src/genkit/core/action.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/packages/genkit/src/genkit/core/reflection.py b/py/packages/genkit/src/genkit/core/reflection.py index b1366d1e6..b95b5f9eb 100644 --- a/py/packages/genkit/src/genkit/core/reflection.py +++ b/py/packages/genkit/src/genkit/core/reflection.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/packages/genkit/src/genkit/core/registry.py b/py/packages/genkit/src/genkit/core/registry.py index ac69e723c..54d1005da 100644 --- a/py/packages/genkit/src/genkit/core/registry.py +++ b/py/packages/genkit/src/genkit/core/registry.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/packages/genkit/src/genkit/core/tracing.py b/py/packages/genkit/src/genkit/core/tracing.py index 8fca4d312..d135b2291 100644 --- a/py/packages/genkit/src/genkit/core/tracing.py +++ b/py/packages/genkit/src/genkit/core/tracing.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/packages/genkit/src/genkit/core/types.py b/py/packages/genkit/src/genkit/core/types.py index f7fc0dd96..52f1e3493 100644 --- a/py/packages/genkit/src/genkit/core/types.py +++ b/py/packages/genkit/src/genkit/core/types.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/packages/genkit/src/genkit/veneer/__init__.py b/py/packages/genkit/src/genkit/veneer/__init__.py index 22cdc86fc..75ee04e8d 100644 --- a/py/packages/genkit/src/genkit/veneer/__init__.py +++ b/py/packages/genkit/src/genkit/veneer/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/packages/handlebarz/src/handlebarz/__init__.py b/py/packages/handlebarz/src/handlebarz/__init__.py index 7b5fdf8b2..ee2900800 100644 --- a/py/packages/handlebarz/src/handlebarz/__init__.py +++ b/py/packages/handlebarz/src/handlebarz/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/plugins/chroma/src/genkit/plugins/chroma/__init__.py b/py/plugins/chroma/src/genkit/plugins/chroma/__init__.py index 6678a0a4a..b309c9882 100644 --- a/py/plugins/chroma/src/genkit/plugins/chroma/__init__.py +++ b/py/plugins/chroma/src/genkit/plugins/chroma/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/plugins/firebase/src/genkit/plugins/firebase/__init__.py b/py/plugins/firebase/src/genkit/plugins/firebase/__init__.py index 99e7e023a..6fac4d9f8 100644 --- a/py/plugins/firebase/src/genkit/plugins/firebase/__init__.py +++ b/py/plugins/firebase/src/genkit/plugins/firebase/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/plugins/google-ai/src/genkit/plugins/google_ai/__init__.py b/py/plugins/google-ai/src/genkit/plugins/google_ai/__init__.py index a7ddbf40d..8016d48b8 100644 --- a/py/plugins/google-ai/src/genkit/plugins/google_ai/__init__.py +++ b/py/plugins/google-ai/src/genkit/plugins/google_ai/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/plugins/google-ai/src/genkit/plugins/google_ai/models/__init__.py b/py/plugins/google-ai/src/genkit/plugins/google_ai/models/__init__.py index b9528eeb8..7ff179d41 100644 --- a/py/plugins/google-ai/src/genkit/plugins/google_ai/models/__init__.py +++ b/py/plugins/google-ai/src/genkit/plugins/google_ai/models/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/plugins/google-cloud/src/genkit/plugins/google_cloud/__init__.py b/py/plugins/google-cloud/src/genkit/plugins/google_cloud/__init__.py index cdf27d88f..f2e88a2ed 100644 --- a/py/plugins/google-cloud/src/genkit/plugins/google_cloud/__init__.py +++ b/py/plugins/google-cloud/src/genkit/plugins/google_cloud/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/plugins/ollama/src/genkit/plugins/ollama/__init__.py b/py/plugins/ollama/src/genkit/plugins/ollama/__init__.py index ac9f8e05a..c57811a68 100644 --- a/py/plugins/ollama/src/genkit/plugins/ollama/__init__.py +++ b/py/plugins/ollama/src/genkit/plugins/ollama/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/plugins/ollama/src/genkit/plugins/ollama/models/__init__.py b/py/plugins/ollama/src/genkit/plugins/ollama/models/__init__.py index f06fbcebe..fb5547c09 100644 --- a/py/plugins/ollama/src/genkit/plugins/ollama/models/__init__.py +++ b/py/plugins/ollama/src/genkit/plugins/ollama/models/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/plugins/pinecone/src/genkit/plugins/pinecone/__init__.py b/py/plugins/pinecone/src/genkit/plugins/pinecone/__init__.py index c35b7e1ac..a9589f980 100644 --- a/py/plugins/pinecone/src/genkit/plugins/pinecone/__init__.py +++ b/py/plugins/pinecone/src/genkit/plugins/pinecone/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/__init__.py b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/__init__.py index 307d8dfda..086af974b 100644 --- a/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/__init__.py +++ b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/models/__init__.py b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/models/__init__.py index 40ca43c53..39639eca6 100644 --- a/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/models/__init__.py +++ b/py/plugins/vertex-ai/src/genkit/plugins/vertex_ai/models/__init__.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/samples/hello/hello.py b/py/samples/hello/hello.py index cf379fa7f..658710168 100644 --- a/py/samples/hello/hello.py +++ b/py/samples/hello/hello.py @@ -1,17 +1,4 @@ # 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 diff --git a/py/tests/smoke/package_test.py b/py/tests/smoke/package_test.py index 8fb0983e1..598659dfb 100644 --- a/py/tests/smoke/package_test.py +++ b/py/tests/smoke/package_test.py @@ -1,17 +1,4 @@ # 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 """Smoke tests for package structure.""" From 2ebcd7253e7f122146fab019a3065b90f9ded1ac Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Mon, 3 Feb 2025 15:00:20 -0500 Subject: [PATCH 497/562] chore: update-chatbot-sample to 1.0 (#1802) * chore: update-chatbot-sample to 1.0 --- samples/chatbot/package-lock.json | 354 - samples/{chatbot => js-chatbot}/.gitignore | 0 samples/{chatbot => js-chatbot}/README.md | 8 - samples/{chatbot => js-chatbot}/eval.json | 0 .../genkit-app/.editorconfig | 0 .../genkit-app/.gitignore | 0 .../genkit-app/README.md | 0 .../genkit-app/angular.json | 0 .../js-chatbot/genkit-app/package-lock.json | 15809 ++++++++++++++++ .../genkit-app/package.json | 6 +- .../genkit-app/public/favicon.ico | Bin .../genkit-app/src/app/app.component.html | 0 .../genkit-app/src/app/app.component.scss | 0 .../genkit-app/src/app/app.component.spec.ts | 0 .../genkit-app/src/app/app.component.ts | 0 .../genkit-app/src/app/app.config.ts | 0 .../genkit-app/src/app/app.routes.ts | 0 .../samples/chatbot/chatbot.component.html | 0 .../samples/chatbot/chatbot.component.scss | 0 .../samples/chatbot/chatbot.component.spec.ts | 0 .../app/samples/chatbot/chatbot.component.ts | 0 .../genkit-app/src/index.html | 0 .../genkit-app/src/main.ts | 0 .../genkit-app/src/styles.scss | 0 .../genkit-app/tsconfig.app.json | 0 .../genkit-app/tsconfig.json | 0 .../genkit-app/tsconfig.spec.json | 0 samples/js-chatbot/package-lock.json | 5378 ++++++ samples/{chatbot => js-chatbot}/package.json | 3 +- samples/js-chatbot/server/package-lock.json | 6375 +++++++ .../server/package.json | 7 +- .../server/src/index.ts | 11 +- .../server/src/memory.ts | 0 .../server/tsconfig.json | 0 34 files changed, 27577 insertions(+), 374 deletions(-) delete mode 100644 samples/chatbot/package-lock.json rename samples/{chatbot => js-chatbot}/.gitignore (100%) rename samples/{chatbot => js-chatbot}/README.md (84%) rename samples/{chatbot => js-chatbot}/eval.json (100%) rename samples/{chatbot => js-chatbot}/genkit-app/.editorconfig (100%) rename samples/{chatbot => js-chatbot}/genkit-app/.gitignore (100%) rename samples/{chatbot => js-chatbot}/genkit-app/README.md (100%) rename samples/{chatbot => js-chatbot}/genkit-app/angular.json (100%) create mode 100644 samples/js-chatbot/genkit-app/package-lock.json rename samples/{chatbot => js-chatbot}/genkit-app/package.json (93%) rename samples/{chatbot => js-chatbot}/genkit-app/public/favicon.ico (100%) rename samples/{chatbot => js-chatbot}/genkit-app/src/app/app.component.html (100%) rename samples/{chatbot => js-chatbot}/genkit-app/src/app/app.component.scss (100%) rename samples/{chatbot => js-chatbot}/genkit-app/src/app/app.component.spec.ts (100%) rename samples/{chatbot => js-chatbot}/genkit-app/src/app/app.component.ts (100%) rename samples/{chatbot => js-chatbot}/genkit-app/src/app/app.config.ts (100%) rename samples/{chatbot => js-chatbot}/genkit-app/src/app/app.routes.ts (100%) rename samples/{chatbot => js-chatbot}/genkit-app/src/app/samples/chatbot/chatbot.component.html (100%) rename samples/{chatbot => js-chatbot}/genkit-app/src/app/samples/chatbot/chatbot.component.scss (100%) rename samples/{chatbot => js-chatbot}/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts (100%) rename samples/{chatbot => js-chatbot}/genkit-app/src/app/samples/chatbot/chatbot.component.ts (100%) rename samples/{chatbot => js-chatbot}/genkit-app/src/index.html (100%) rename samples/{chatbot => js-chatbot}/genkit-app/src/main.ts (100%) rename samples/{chatbot => js-chatbot}/genkit-app/src/styles.scss (100%) rename samples/{chatbot => js-chatbot}/genkit-app/tsconfig.app.json (100%) rename samples/{chatbot => js-chatbot}/genkit-app/tsconfig.json (100%) rename samples/{chatbot => js-chatbot}/genkit-app/tsconfig.spec.json (100%) create mode 100644 samples/js-chatbot/package-lock.json rename samples/{chatbot => js-chatbot}/package.json (88%) create mode 100644 samples/js-chatbot/server/package-lock.json rename samples/{chatbot => js-chatbot}/server/package.json (81%) rename samples/{chatbot => js-chatbot}/server/src/index.ts (91%) rename samples/{chatbot => js-chatbot}/server/src/memory.ts (100%) rename samples/{chatbot => js-chatbot}/server/tsconfig.json (100%) diff --git a/samples/chatbot/package-lock.json b/samples/chatbot/package-lock.json deleted file mode 100644 index 5f7f4265f..000000000 --- a/samples/chatbot/package-lock.json +++ /dev/null @@ -1,354 +0,0 @@ -{ - "name": "js-angular", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "js-angular", - "version": "1.0.0", - "license": "ISC", - "devDependencies": { - "concurrently": "^8.2.2" - } - }, - "node_modules/@babel/runtime": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", - "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "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==", - "dev": true, - "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==", - "dev": true - }, - "node_modules/concurrently": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", - "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.2", - "date-fns": "^2.30.0", - "lodash": "^4.17.21", - "rxjs": "^7.8.1", - "shell-quote": "^1.8.1", - "spawn-command": "0.0.2", - "supports-color": "^8.1.1", - "tree-kill": "^1.2.2", - "yargs": "^17.7.2" - }, - "bin": { - "conc": "dist/bin/concurrently.js", - "concurrently": "dist/bin/concurrently.js" - }, - "engines": { - "node": "^14.13.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" - } - }, - "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, - "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==", - "dev": true - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/shell-quote": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", - "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/spawn-command": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", - "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", - "dev": true - }, - "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==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "bin": { - "tree-kill": "cli.js" - } - }, - "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==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=12" - } - } - } -} diff --git a/samples/chatbot/.gitignore b/samples/js-chatbot/.gitignore similarity index 100% rename from samples/chatbot/.gitignore rename to samples/js-chatbot/.gitignore diff --git a/samples/chatbot/README.md b/samples/js-chatbot/README.md similarity index 84% rename from samples/chatbot/README.md rename to samples/js-chatbot/README.md index a2f1728bf..34ece718b 100644 --- a/samples/chatbot/README.md +++ b/samples/js-chatbot/README.md @@ -4,7 +4,6 @@ This is a simple chatbot. You can pick which model to use. Prerequisite -- install Genkit (`npm i -g genkit`) - Google Cloud project with Vertex AI API enabled (https://pantheon.corp.google.com/apis/library/aiplatform.googleapis.com) - gcloud CLI installed (https://cloud.google.com/sdk/docs/install-sdk) - to use Llama 3.1 405b enable it in the Vertex AI [Model Garden](https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama3-405b-instruct-maas) @@ -16,13 +15,6 @@ gcloud auth login gcloud auth application-default login --project YOUR_PROJECT ``` -Clone this code - -``` -git clone https://github.com/firebase/genkit -cd genkit/samples/chatbot -``` - Install deps and run the chatbot app ```bash diff --git a/samples/chatbot/eval.json b/samples/js-chatbot/eval.json similarity index 100% rename from samples/chatbot/eval.json rename to samples/js-chatbot/eval.json diff --git a/samples/chatbot/genkit-app/.editorconfig b/samples/js-chatbot/genkit-app/.editorconfig similarity index 100% rename from samples/chatbot/genkit-app/.editorconfig rename to samples/js-chatbot/genkit-app/.editorconfig diff --git a/samples/chatbot/genkit-app/.gitignore b/samples/js-chatbot/genkit-app/.gitignore similarity index 100% rename from samples/chatbot/genkit-app/.gitignore rename to samples/js-chatbot/genkit-app/.gitignore diff --git a/samples/chatbot/genkit-app/README.md b/samples/js-chatbot/genkit-app/README.md similarity index 100% rename from samples/chatbot/genkit-app/README.md rename to samples/js-chatbot/genkit-app/README.md diff --git a/samples/chatbot/genkit-app/angular.json b/samples/js-chatbot/genkit-app/angular.json similarity index 100% rename from samples/chatbot/genkit-app/angular.json rename to samples/js-chatbot/genkit-app/angular.json diff --git a/samples/js-chatbot/genkit-app/package-lock.json b/samples/js-chatbot/genkit-app/package-lock.json new file mode 100644 index 000000000..c0abeeebd --- /dev/null +++ b/samples/js-chatbot/genkit-app/package-lock.json @@ -0,0 +1,15809 @@ +{ + "name": "genkit-chat-app", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "genkit-chat-app", + "version": "0.1.0", + "dependencies": { + "@angular/animations": "^18.0.0", + "@angular/cdk": "^18.0.1", + "@angular/common": "^18.0.0", + "@angular/compiler": "^18.0.0", + "@angular/core": "^18.0.0", + "@angular/forms": "^18.0.0", + "@angular/material": "^18.0.1", + "@angular/platform-browser": "^18.0.0", + "@angular/platform-browser-dynamic": "^18.0.0", + "@angular/router": "^18.0.0", + "genkit": "^1.0.0-rc.14", + "marked": "^12.0.2", + "ngx-markdown": "^18.0.0", + "prismjs": "^1.29.0", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.14.3" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^18.0.2", + "@angular/cli": "^18.0.2", + "@angular/compiler-cli": "^18.0.0", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.1.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.4.2" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1802.14", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.14.tgz", + "integrity": "sha512-eplaGCXSlPwf1f4XwyzsYTd8/lJ0/Adm6XsODsBxvkZlIpLcps80/h2lH5MVJpoDREzIFu1BweDpYCoNK5yYZg==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "18.2.14", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/build-angular": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.14.tgz", + "integrity": "sha512-ycie4OhvNv8eNVqvq46pCIf6kB50xbMOdnAVqmlj+BaQjWbGjUQPjAmp4VGqeDZZ/lW82xkfTmJZxc6pYp7YdQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1802.14", + "@angular-devkit/build-webpack": "0.1802.14", + "@angular-devkit/core": "18.2.14", + "@angular/build": "18.2.14", + "@babel/core": "7.25.2", + "@babel/generator": "7.25.0", + "@babel/helper-annotate-as-pure": "7.24.7", + "@babel/helper-split-export-declaration": "7.24.7", + "@babel/plugin-transform-async-generator-functions": "7.25.0", + "@babel/plugin-transform-async-to-generator": "7.24.7", + "@babel/plugin-transform-runtime": "7.24.7", + "@babel/preset-env": "7.25.3", + "@babel/runtime": "7.25.0", + "@discoveryjs/json-ext": "0.6.1", + "@ngtools/webpack": "18.2.14", + "@vitejs/plugin-basic-ssl": "1.1.0", + "ansi-colors": "4.1.3", + "autoprefixer": "10.4.20", + "babel-loader": "9.1.3", + "browserslist": "^4.21.5", + "copy-webpack-plugin": "12.0.2", + "critters": "0.0.24", + "css-loader": "7.1.2", + "esbuild-wasm": "0.23.0", + "fast-glob": "3.3.2", + "http-proxy-middleware": "3.0.3", + "https-proxy-agent": "7.0.5", + "istanbul-lib-instrument": "6.0.3", + "jsonc-parser": "3.3.1", + "karma-source-map-support": "1.4.0", + "less": "4.2.0", + "less-loader": "12.2.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.3.1", + "magic-string": "0.30.11", + "mini-css-extract-plugin": "2.9.0", + "mrmime": "2.0.0", + "open": "10.1.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "7.0.0", + "picomatch": "4.0.2", + "piscina": "4.6.1", + "postcss": "8.4.41", + "postcss-loader": "8.1.1", + "resolve-url-loader": "5.0.0", + "rxjs": "7.8.1", + "sass": "1.77.6", + "sass-loader": "16.0.0", + "semver": "7.6.3", + "source-map-loader": "5.0.0", + "source-map-support": "0.5.21", + "terser": "5.31.6", + "tree-kill": "1.2.2", + "tslib": "2.6.3", + "watchpack": "2.4.1", + "webpack": "5.94.0", + "webpack-dev-middleware": "7.4.2", + "webpack-dev-server": "5.0.4", + "webpack-merge": "6.0.1", + "webpack-subresource-integrity": "5.1.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.23.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", + "@web/test-runner": "^0.18.0", + "browser-sync": "^3.0.2", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", + "karma": "^6.3.0", + "ng-packagr": "^18.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=5.4 <5.6" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "@web/test-runner": { + "optional": true + }, + "browser-sync": { + "optional": true + }, + "jest": { + "optional": true + }, + "jest-environment-jsdom": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1802.14", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.14.tgz", + "integrity": "sha512-cccne0SG4BaQHsKRRZCi/wMLJ7yFXrwvE8w+Kug3HdpJJoyH3FeG386EQuca/azslQlK+c5g4ywSZdXeNkGazA==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1802.14", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^5.0.2" + } + }, + "node_modules/@angular-devkit/core": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.14.tgz", + "integrity": "sha512-UGIGOjXuOyCW+5S4tINu7e6LOu738CmTw3h7Ui1I8OzdTIYJcYJrei8sgrwDwOYADRal+p0MeMlnykH3TM5XBA==", + "dev": true, + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.14.tgz", + "integrity": "sha512-mukjZIHHB7gWratq8fZwUq5WZ+1bF4feG/idXr1wgQ+/FqWjs2PP7HDesHVcPymmRulpTyCpB7TNB1O1fgnCpA==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "18.2.14", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.11", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/animations": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.13.tgz", + "integrity": "sha512-rG5J5Ek5Hg+Tz2NjkNOaG6PupiNK/lPfophXpsR1t/nWujqnMWX2krahD/i6kgD+jNWNKCJCYSOVvCx/BHOtKA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "18.2.13" + } + }, + "node_modules/@angular/build": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.14.tgz", + "integrity": "sha512-9g24Oe/ZLULacW3hEpRCjSZIJPJTzN5BeFbA27epSV5NsrQOoeUGsEpRs90Zmt6eReO0fW1BGshWRoZtpSedcw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1802.14", + "@babel/core": "7.25.2", + "@babel/helper-annotate-as-pure": "7.24.7", + "@babel/helper-split-export-declaration": "7.24.7", + "@babel/plugin-syntax-import-attributes": "7.24.7", + "@inquirer/confirm": "3.1.22", + "@vitejs/plugin-basic-ssl": "1.1.0", + "browserslist": "^4.23.0", + "critters": "0.0.24", + "esbuild": "0.23.0", + "fast-glob": "3.3.2", + "https-proxy-agent": "7.0.5", + "listr2": "8.2.4", + "lmdb": "3.0.13", + "magic-string": "0.30.11", + "mrmime": "2.0.0", + "parse5-html-rewriting-stream": "7.0.0", + "picomatch": "4.0.2", + "piscina": "4.6.1", + "rollup": "4.22.4", + "sass": "1.77.6", + "semver": "7.6.3", + "vite": "5.4.14", + "watchpack": "2.4.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", + "less": "^4.2.0", + "postcss": "^8.4.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=5.4 <5.6" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "less": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular/cdk": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.14.tgz", + "integrity": "sha512-vDyOh1lwjfVk9OqoroZAP8pf3xxKUvyl+TVR8nJxL4c5fOfUFkD7l94HaanqKSRwJcI2xiztuu92IVoHn8T33Q==", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/cli": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.14.tgz", + "integrity": "sha512-kWgRRQtJPkr8iwN7DMbTi3sXOnv7H5QhbU/GgD3nNX3D8YCSPmnby4PAE/P3wn7FsIK9JsSchsCt7MZ37Urh9A==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1802.14", + "@angular-devkit/core": "18.2.14", + "@angular-devkit/schematics": "18.2.14", + "@inquirer/prompts": "5.3.8", + "@listr2/prompt-adapter-inquirer": "2.0.15", + "@schematics/angular": "18.2.14", + "@yarnpkg/lockfile": "1.1.0", + "ini": "4.1.3", + "jsonc-parser": "3.3.1", + "listr2": "8.2.4", + "npm-package-arg": "11.0.3", + "npm-pick-manifest": "9.1.0", + "pacote": "18.0.6", + "resolve": "1.22.8", + "semver": "7.6.3", + "symbol-observable": "4.0.0", + "yargs": "17.7.2" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/common": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.13.tgz", + "integrity": "sha512-4ZqrNp1PoZo7VNvW+sbSc2CB2axP1sCH2wXl8B0wdjsj8JY1hF1OhuugwhpAHtGxqewed2kCXayE+ZJqSTV4jw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "18.2.13", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.13.tgz", + "integrity": "sha512-TzWcrkopyjFF+WeDr2cRe8CcHjU72KfYV3Sm2TkBkcXrkYX5sDjGWrBGrG3hRB4e4okqchrOCvm1MiTdy2vKMA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "18.2.13" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + } + } + }, + "node_modules/@angular/compiler-cli": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.13.tgz", + "integrity": "sha512-DBSh4AQwkiJDSiVvJATRmjxf6wyUs9pwQLgaFdSlfuTRO+sdb0J2z1r3BYm8t0IqdoyXzdZq2YCH43EmyvD71g==", + "dev": true, + "dependencies": { + "@babel/core": "7.25.2", + "@jridgewell/sourcemap-codec": "^1.4.14", + "chokidar": "^4.0.0", + "convert-source-map": "^1.5.1", + "reflect-metadata": "^0.2.0", + "semver": "^7.0.0", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js", + "ngcc": "bundles/ngcc/index.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/compiler": "18.2.13", + "typescript": ">=5.4 <5.6" + } + }, + "node_modules/@angular/compiler-cli/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@angular/compiler-cli/node_modules/readdirp": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", + "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@angular/core": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.13.tgz", + "integrity": "sha512-8mbWHMgO95OuFV1Ejy4oKmbe9NOJ3WazQf/f7wks8Bck7pcihd0IKhlPBNjFllbF5o+04EYSwFhEtvEgjMDClA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.14.10" + } + }, + "node_modules/@angular/forms": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.13.tgz", + "integrity": "sha512-A67D867fu3DSBhdLWWZl/F5pr7v2+dRM2u3U7ZJ0ewh4a+sv+0yqWdJW+a8xIoiHxS+btGEJL2qAKJiH+MCFfg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "18.2.13", + "@angular/core": "18.2.13", + "@angular/platform-browser": "18.2.13", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/material": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.14.tgz", + "integrity": "sha512-28pxzJP49Mymt664WnCtPkKeg7kXUsQKTKGf/Kl95rNTEdTJLbnlcc8wV0rT0yQNR7kXgpfBnG7h0ETLv/iu5Q==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": "^18.0.0 || ^19.0.0", + "@angular/cdk": "18.2.14", + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", + "@angular/forms": "^18.0.0 || ^19.0.0", + "@angular/platform-browser": "^18.0.0 || ^19.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.13.tgz", + "integrity": "sha512-tu7ZzY6qD3ATdWFzcTcsAKe7M6cJeWbT/4/bF9unyGO3XBPcNYDKoiz10+7ap2PUd0fmPwvuvTvSNJiFEBnB8Q==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/animations": "18.2.13", + "@angular/common": "18.2.13", + "@angular/core": "18.2.13" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.13.tgz", + "integrity": "sha512-kbQCf9+8EpuJC7buBxhSiwBtXvjAwAKh6MznD6zd2pyCYqfY6gfRCZQRtK59IfgVtKmEONWI9grEyNIRoTmqJg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "18.2.13", + "@angular/compiler": "18.2.13", + "@angular/core": "18.2.13", + "@angular/platform-browser": "18.2.13" + } + }, + "node_modules/@angular/router": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.13.tgz", + "integrity": "sha512-VKmfgi/r/CkyBq9nChQ/ptmfu0JT/8ONnLVJ5H+SkFLRYJcIRyHLKjRihMCyVm6xM5yktOdCaW73NTQrFz7+bg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "18.2.13", + "@angular/core": "18.2.13", + "@angular/platform-browser": "18.2.13", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.4.1.tgz", + "integrity": "sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==", + "optional": true, + "dependencies": { + "package-manager-detector": "^0.2.0", + "tinyexec": "^0.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@antfu/utils": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz", + "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", + "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz", + "integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", + "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", + "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.26.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", + "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz", + "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-remap-async-to-generator": "^7.25.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/traverse": "^7.25.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", + "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-remap-async-to-generator": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", + "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", + "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.2", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.24.7", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.0", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoped-functions": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.0", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", + "@babel/plugin-transform-dotall-regex": "^7.24.7", + "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", + "@babel/plugin-transform-dynamic-import": "^7.24.7", + "@babel/plugin-transform-exponentiation-operator": "^7.24.7", + "@babel/plugin-transform-export-namespace-from": "^7.24.7", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", + "@babel/plugin-transform-json-strings": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", + "@babel/plugin-transform-member-expression-literals": "^7.24.7", + "@babel/plugin-transform-modules-amd": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", + "@babel/plugin-transform-modules-umd": "^7.24.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-new-target": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.7", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-object-super": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-property-literals": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-reserved-words": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.7", + "@babel/plugin-transform-spread": "^7.24.7", + "@babel/plugin-transform-sticky-regex": "^7.24.7", + "@babel/plugin-transform-template-literals": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.8", + "@babel/plugin-transform-unicode-escapes": "^7.24.7", + "@babel/plugin-transform-unicode-property-regex": "^7.24.7", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.37.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", + "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/types": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz", + "integrity": "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==", + "optional": true + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "optional": true, + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "optional": true, + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "optional": true + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "optional": true + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "optional": true + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.1.tgz", + "integrity": "sha512-boghen8F0Q8D+0/Q1/1r6DUEieUJ8w2a1gIknExMSHBsJFOr2+0KUfHiVYBvucPwl3+RU5PFBK833FjFCh3BhA==", + "dev": true, + "engines": { + "node": ">=14.17.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", + "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", + "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", + "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", + "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", + "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", + "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", + "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", + "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", + "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", + "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", + "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", + "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", + "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", + "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", + "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", + "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", + "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", + "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", + "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", + "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", + "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", + "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", + "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", + "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@genkit-ai/ai": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.14.tgz", + "integrity": "sha512-5CHyRixumiM5uHkzwHl7IWbrYSzWvhDvYAsMoq9mmI8iCJA9g/3u8JOcHSRiseuaap5gv/QGm809IpIlak55ZA==", + "dependencies": { + "@genkit-ai/core": "1.0.0-rc.14", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.11.19", + "colorette": "^2.0.20", + "dotprompt": "^1.0.0-dev.3 || ^1", + "json5": "^2.2.3", + "node-fetch": "^3.3.2", + "partial-json": "^0.1.7", + "uuid": "^10.0.0" + } + }, + "node_modules/@genkit-ai/ai/node_modules/@types/node": { + "version": "20.17.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", + "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@genkit-ai/ai/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/@genkit-ai/core": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.14.tgz", + "integrity": "sha512-8h0Vl3SCP0vYIN60uELaZWEAP6BjLxXTcoWv2MFn7khxblzVkMWq98IxPThHo3s5BGB+Pxnhnsywdb/ItliFUA==", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "@types/json-schema": "^7.0.15", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "async-mutex": "^0.5.0", + "body-parser": "^1.20.3", + "cors": "^2.8.5", + "dotprompt": "^1.0.0-dev.3 || ^1", + "express": "^4.21.0", + "get-port": "^5.1.0", + "json-schema": "^0.4.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz", + "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "optional": true + }, + "node_modules/@iconify/utils": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.2.1.tgz", + "integrity": "sha512-0/7J7hk4PqXmxo5PDBDxmnecw5PxklZJfNjIVG9FM0mEfVrvfudS22rYWsqVk6gR3UJ/mSYS90X4R3znXnqfNA==", + "optional": true, + "dependencies": { + "@antfu/install-pkg": "^0.4.1", + "@antfu/utils": "^0.7.10", + "@iconify/types": "^2.0.0", + "debug": "^4.4.0", + "globals": "^15.13.0", + "kolorist": "^1.8.0", + "local-pkg": "^0.5.1", + "mlly": "^1.7.3" + } + }, + "node_modules/@iconify/utils/node_modules/globals": { + "version": "15.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", + "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", + "optional": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.5.0.tgz", + "integrity": "sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/confirm": { + "version": "3.1.22", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.22.tgz", + "integrity": "sha512-gsAKIOWBm2Q87CDfs9fEo7wJT3fwWIJfnDGMn9Qy74gBnNFOACDNfhUzovubbJjWnKLGBln7/NcSmZwj5DuEXg==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", + "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "dev": true, + "dependencies": { + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.5", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/@inquirer/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", + "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", + "dev": true, + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.2.0.tgz", + "integrity": "sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/expand": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.3.0.tgz", + "integrity": "sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.10.tgz", + "integrity": "sha512-Ey6176gZmeqZuY/W/nZiUyvmb1/qInjcpiZjXWi6nON+nxJpD1bxtSoBxNliGISae32n6OwbY+TSXPZ1CfS4bw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.3.0.tgz", + "integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-1.1.0.tgz", + "integrity": "sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/password": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.2.0.tgz", + "integrity": "sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/prompts": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.3.8.tgz", + "integrity": "sha512-b2BudQY/Si4Y2a0PdZZL6BeJtl8llgeZa7U2j47aaJSCeAl1e4UI7y8a9bSkO3o/ZbZrgT5muy/34JbsjfIWxA==", + "dev": true, + "dependencies": { + "@inquirer/checkbox": "^2.4.7", + "@inquirer/confirm": "^3.1.22", + "@inquirer/editor": "^2.1.22", + "@inquirer/expand": "^2.1.22", + "@inquirer/input": "^2.2.9", + "@inquirer/number": "^1.0.10", + "@inquirer/password": "^2.1.22", + "@inquirer/rawlist": "^2.2.4", + "@inquirer/search": "^1.0.7", + "@inquirer/select": "^2.4.7" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/rawlist": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.3.0.tgz", + "integrity": "sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/search": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-1.1.0.tgz", + "integrity": "sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/select": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.5.0.tgz", + "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", + "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", + "dev": true, + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.1.tgz", + "integrity": "sha512-osjeBqMJ2lb/j/M8NCPjs1ylqWIcTRTycIhVB5pt6LgzgeRSb0YRZ7j9RfA8wIUrsr/medIuhVyonXRZWLyfdw==", + "dev": true, + "dependencies": { + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.1.2", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz", + "integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true + }, + "node_modules/@listr2/prompt-adapter-inquirer": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.15.tgz", + "integrity": "sha512-MZrGem/Ujjd4cPTLYDfCZK2iKKeiO/8OX13S6jqxldLs0Prf2aGqVlJ77nMBqMv7fzqgXEgjrNHLXcKR8l9lOg==", + "dev": true, + "dependencies": { + "@inquirer/type": "^1.5.1" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@inquirer/prompts": ">= 3 < 6" + } + }, + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.0.13.tgz", + "integrity": "sha512-uiKPB0Fv6WEEOZjruu9a6wnW/8jrjzlZbxXscMB8kuCJ1k6kHpcBnuvaAWcqhbI7rqX5GKziwWEdD+wi2gNLfA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.0.13.tgz", + "integrity": "sha512-bEVIIfK5mSQoG1R19qA+fJOvCB+0wVGGnXHT3smchBVahYBdlPn2OsZZKzlHWfb1E+PhLBmYfqB5zQXFP7hJig==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.0.13.tgz", + "integrity": "sha512-Yml1KlMzOnXj/tnW7yX8U78iAzTk39aILYvCPbqeewAq1kSzl+w59k/fiVkTBfvDi/oW/5YRxL+Fq+Y1Fr1r2Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.0.13.tgz", + "integrity": "sha512-afbVrsMgZ9dUTNUchFpj5VkmJRxvht/u335jUJ7o23YTbNbnpmXif3VKQGCtnjSh+CZaqm6N3CPG8KO3zwyZ1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.0.13.tgz", + "integrity": "sha512-vOtxu0xC0SLdQ2WRXg8Qgd8T32ak4SPqk5zjItRszrJk2BdeXqfGxBJbP7o4aOvSPSmSSv46Lr1EP4HXU8v7Kg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.0.13.tgz", + "integrity": "sha512-UCrMJQY/gJnOl3XgbWRZZUvGGBuKy6i0YNSptgMzHBjs+QYDYR1Mt/RLTOPy4fzzves65O1EDmlL//OzEqoLlA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@mermaid-js/parser": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.3.0.tgz", + "integrity": "sha512-HsvL6zgE5sUPGgkIDlmAWR1HTNHz2Iy11BAWPTa4Jjabkpguy4Ze2gzfLrg6pdRuBvFwgUYyxiaNqZwrEEXepA==", + "optional": true, + "dependencies": { + "langium": "3.0.0" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@ngtools/webpack": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.14.tgz", + "integrity": "sha512-rT+Y4WR8QTVsijtb+YRqHcPTpd1ZiwRbklQXRTxU0YGFHpxpi+bhjmY8FjpPoAtdPO1Lg3l3KIZPZa0thG0FNg==", + "dev": true, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "typescript": ">=5.4 <5.6", + "webpack": "^5.54.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/agent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", + "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/@npmcli/fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz", + "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^4.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", + "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", + "dev": true, + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", + "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.1.tgz", + "integrity": "sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ==", + "dev": true, + "dependencies": { + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^4.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/package-json/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", + "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", + "dev": true, + "dependencies": { + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", + "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", + "dev": true, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", + "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "proc-log": "^4.0.0", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz", + "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz", + "integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", + "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz", + "integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz", + "integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz", + "integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", + "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", + "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz", + "integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", + "@opentelemetry/exporter-zipkin": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/sdk-trace-node": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", + "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.25.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/propagator-b3": "1.25.1", + "@opentelemetry/propagator-jaeger": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", + "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", + "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", + "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", + "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", + "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", + "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", + "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", + "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", + "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", + "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", + "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", + "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", + "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", + "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", + "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", + "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@schematics/angular": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.14.tgz", + "integrity": "sha512-CHh6ew2Az71UlvVcnYeuMEwjwkZqR7y/9ebLzFRvczC71ZL8qPVBpBTVGbCpGBd54VEbCZVWRxBQoZZ5LP/aBw==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "18.2.14", + "@angular-devkit/schematics": "18.2.14", + "jsonc-parser": "3.3.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@sigstore/bundle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", + "integrity": "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==", + "dev": true, + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.1.0.tgz", + "integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==", + "dev": true, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.3.tgz", + "integrity": "sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ==", + "dev": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.3.2.tgz", + "integrity": "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==", + "dev": true, + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", + "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", + "dev": true, + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.2.1.tgz", + "integrity": "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==", + "dev": true, + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true + }, + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "dev": true, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.1.tgz", + "integrity": "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==", + "dev": true, + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "optional": true, + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "optional": true + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "optional": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "optional": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "optional": true + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "optional": true + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "optional": true, + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "optional": true + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==", + "optional": true + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "optional": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "optional": true + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "optional": true + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "optional": true, + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "optional": true + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "optional": true + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "optional": true, + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "optional": true + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "optional": true, + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==", + "optional": true + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "optional": true + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "optional": true + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "optional": true + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "optional": true, + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "optional": true + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "optional": true + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "optional": true, + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "optional": true + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "optional": true + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "optional": true + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "optional": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "optional": true, + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "optional": true + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/http-proxy": { + "version": "1.17.15", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jasmine": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.5.tgz", + "integrity": "sha512-SaCZ3kM5NjOiJqMRYwHpLbTfUC2Dyk1KS3QanNFsUYPGTk70CWVK/J9ueun6zNhw/UkgV7xl8V4ZLQZNRbfnNw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", + "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==" + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true + }, + "node_modules/@types/ws": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", + "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vitejs/plugin-basic-ssl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", + "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", + "dev": true, + "engines": { + "node": ">=14.6.0" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "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==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/babel-loader": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", + "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", + "dev": true, + "dependencies": { + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz", + "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.3", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz", + "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.3" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/bonjour-service": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", + "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "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==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "18.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", + "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001696", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001696.tgz", + "integrity": "sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "optional": true, + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "optional": true, + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/clipboard": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz", + "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==", + "optional": true, + "dependencies": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/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==" + }, + "node_modules/cliui/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-deep/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.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==", + "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==" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz", + "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.0.2", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "optional": true + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/connect/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", + "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", + "dev": true, + "dependencies": { + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.1", + "globby": "^14.0.0", + "normalize-path": "^3.0.0", + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/core-js-compat": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz", + "integrity": "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.24.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "optional": true, + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/critters": { + "version": "0.0.24", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.24.tgz", + "integrity": "sha512-Oyqew0FGM0wYUSNqR0L6AteO5MpMoUU0rhKRieXeiKs+PmRTxiJMyaunYB2KF6fQ3dzChXKCpbFOEJx3OQ1v/Q==", + "deprecated": "Ownership of Critters has moved to the Nuxt team, who will be maintaining the project going forward. If you'd like to keep using Critters, please switch to the actively-maintained fork at https://github.com/danielroe/beasties", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^5.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.2", + "htmlparser2": "^8.0.2", + "postcss": "^8.4.23", + "postcss-media-query-parser": "^0.2.3" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-loader": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "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-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "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==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true + }, + "node_modules/cytoscape": { + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.31.0.tgz", + "integrity": "sha512-zDGn1K/tfZwEnoGOcHc0H4XazqAAXAuDpcYw9mUnUjATjqljyCNGJv8uEvbvxGaGHaVshxMecyl6oc6uKzRfbw==", + "optional": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "optional": true, + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "optional": true, + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "optional": true, + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "optional": true + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "optional": true, + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "optional": true, + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "optional": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "optional": true, + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "optional": true, + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "optional": true, + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "optional": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "optional": true, + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "optional": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-dsv/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "optional": true, + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "optional": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "optional": true, + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "optional": true, + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "optional": true, + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "optional": true, + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "optional": true + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "optional": true, + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "optional": true + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "optional": true, + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "optional": true, + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "optional": true, + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "optional": true, + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "optional": true, + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "optional": true, + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "optional": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.11.tgz", + "integrity": "sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw==", + "optional": true, + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "optional": true + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "optional": true, + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", + "optional": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "dev": true + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "dev": true, + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "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==", + "dev": true, + "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/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dompurify": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", + "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", + "optional": true, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "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==", + "dev": true, + "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/dotprompt": { + "version": "1.0.0-dev.3", + "resolved": "https://registry.npmjs.org/dotprompt/-/dotprompt-1.0.0-dev.3.tgz", + "integrity": "sha512-gAcIhG+vzZlrZcBN/lO8gEMUtXHKMLbs1+snJuZrPkZnNgnWrzz1hYN4vYotpng/5fHsYSPUEckECy6qUNSrsQ==", + "dependencies": { + "handlebars": "^4.7.8", + "yaml": "^2.5.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.90", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.90.tgz", + "integrity": "sha512-C3PN4aydfW91Natdyd449Kw+BzhLmof6tzy5W1pFC5SpQxVXT+oyiyOG9AgYYSN9OdA/ik3YkCrpwqI8ug5Tug==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/emoji-toolkit": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/emoji-toolkit/-/emoji-toolkit-9.0.1.tgz", + "integrity": "sha512-sMMNqKNLVHXJfIKoPbrRJwtYuysVNC9GlKetr72zE3SSVbHqoeDLWVrxP0uM0AE0qvdl3hbUk+tJhhwXZrDHaw==", + "optional": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "dev": true, + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", + "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.2.tgz", + "integrity": "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "punycode": "^1.4.1", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "devOptional": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", + "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.0", + "@esbuild/android-arm": "0.23.0", + "@esbuild/android-arm64": "0.23.0", + "@esbuild/android-x64": "0.23.0", + "@esbuild/darwin-arm64": "0.23.0", + "@esbuild/darwin-x64": "0.23.0", + "@esbuild/freebsd-arm64": "0.23.0", + "@esbuild/freebsd-x64": "0.23.0", + "@esbuild/linux-arm": "0.23.0", + "@esbuild/linux-arm64": "0.23.0", + "@esbuild/linux-ia32": "0.23.0", + "@esbuild/linux-loong64": "0.23.0", + "@esbuild/linux-mips64el": "0.23.0", + "@esbuild/linux-ppc64": "0.23.0", + "@esbuild/linux-riscv64": "0.23.0", + "@esbuild/linux-s390x": "0.23.0", + "@esbuild/linux-x64": "0.23.0", + "@esbuild/netbsd-x64": "0.23.0", + "@esbuild/openbsd-arm64": "0.23.0", + "@esbuild/openbsd-x64": "0.23.0", + "@esbuild/sunos-x64": "0.23.0", + "@esbuild/win32-arm64": "0.23.0", + "@esbuild/win32-ia32": "0.23.0", + "@esbuild/win32-x64": "0.23.0" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.23.0.tgz", + "integrity": "sha512-6jP8UmWy6R6TUUV8bMuC3ZyZ6lZKI56x0tkxyCIqWwRRJ/DgeQKneh/Oid5EoGoPFLrGNkz47ZEtWAYuiY/u9g==", + "dev": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "dev": true + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fastq": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", + "dev": true, + "dependencies": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dev": true, + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/genkit": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.14.tgz", + "integrity": "sha512-JB6fTcJ5CI4UhsP4uNyXpi9/OatyhPwOkBXnbHafE5rdibXuKUXadbb2LC+PWfG3+7WrLXGKlrj7kOIN3Hq2lw==", + "dependencies": { + "@genkit-ai/ai": "1.0.0-rc.14", + "@genkit-ai/core": "1.0.0-rc.14", + "uuid": "^10.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", + "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==", + "optional": true, + "dependencies": { + "delegate": "^3.1.2" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "optional": true + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz", + "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-middleware": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.3.tgz", + "integrity": "sha512-usY0HG5nyDUwtqpiZdETNbmKtw3QQ1jwYFZ9wi5iHzX2BcILwQKtYDJPo7XHTsu5Z0B2Hj3W9NNnbd+AjFWjqg==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.15", + "debug": "^4.3.6", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.3", + "is-plain-object": "^5.0.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "engines": { + "node": ">=10.18" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", + "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", + "dev": true, + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-in-the-middle": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.12.0.tgz", + "integrity": "sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "optional": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "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-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "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-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true + }, + "node_modules/is-network-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", + "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jasmine-core": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.2.tgz", + "integrity": "sha512-2oIUMGn00FdUiqz6epiiJr7xcFyNYj3rDcfmnzfkBnHyBQ3cBQUs4mmyGsOb7TTLb9kxk7dBcmEmqhDKkBoDyA==", + "dev": true + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/karma": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", + "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", + "dev": true, + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.7.2", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-chrome-launcher": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz", + "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==", + "dev": true, + "dependencies": { + "which": "^1.2.1" + } + }, + "node_modules/karma-coverage": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.1.tgz", + "integrity": "sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.0.5", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/karma-coverage/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma-coverage/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/karma-jasmine": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", + "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", + "dev": true, + "dependencies": { + "jasmine-core": "^4.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "karma": "^6.0.0" + } + }, + "node_modules/karma-jasmine-html-reporter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz", + "integrity": "sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==", + "dev": true, + "peerDependencies": { + "jasmine-core": "^4.0.0 || ^5.0.0", + "karma": "^6.0.0", + "karma-jasmine": "^5.0.0" + } + }, + "node_modules/karma-jasmine/node_modules/jasmine-core": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.6.1.tgz", + "integrity": "sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==", + "dev": true + }, + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "dependencies": { + "source-map-support": "^0.5.5" + } + }, + "node_modules/karma/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/karma/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==", + "dev": true + }, + "node_modules/karma/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/karma/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/karma/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/katex": { + "version": "0.16.21", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz", + "integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "optional": true, + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==", + "optional": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "optional": true + }, + "node_modules/langium": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.0.0.tgz", + "integrity": "sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg==", + "optional": true, + "dependencies": { + "chevrotain": "~11.0.3", + "chevrotain-allstar": "~0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.0.8" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/launch-editor": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "optional": true + }, + "node_modules/less": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", + "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.2.0.tgz", + "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==", + "dev": true, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/listr2": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", + "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", + "dev": true, + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "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/lmdb": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.0.13.tgz", + "integrity": "sha512-UGe+BbaSUQtAMZobTb4nHvFMrmvuAQKSeaqAX2meTEQjfsbpl5sxdHD8T72OnwD4GU9uwNhYXIVe4QGs8N9Zyw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "msgpackr": "^1.10.2", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.2.2", + "ordered-binary": "^1.4.1", + "weak-lru-cache": "^1.2.2" + }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "3.0.13", + "@lmdb/lmdb-darwin-x64": "3.0.13", + "@lmdb/lmdb-linux-arm": "3.0.13", + "@lmdb/lmdb-linux-arm64": "3.0.13", + "@lmdb/lmdb-linux-x64": "3.0.13", + "@lmdb/lmdb-win32-x64": "3.0.13" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/local-pkg": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", + "optional": true, + "dependencies": { + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "optional": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "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/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "dev": true, + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/long": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-fetch-happen": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", + "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", + "dev": true, + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.0.tgz", + "integrity": "sha512-4eirfZ7thblFmqFjywlTmuWVSvccHAJbn1r8qQLzmTO11qcqpohOjmY2mFce6x7x7WtskzRqApPD0hv+Oa74jg==", + "dev": true, + "dependencies": { + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.3.0", + "tree-dump": "^1.0.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">= 4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.4.1.tgz", + "integrity": "sha512-Mb01JT/x6CKDWaxigwfZYuYmDZ6xtrNwNlidKZwkSrDaY9n90tdrJTV5Umk+wP1fZscGptmKFXHsXMDEVZ+Q6A==", + "optional": true, + "dependencies": { + "@braintree/sanitize-url": "^7.0.1", + "@iconify/utils": "^2.1.32", + "@mermaid-js/parser": "^0.3.0", + "@types/d3": "^7.4.3", + "cytoscape": "^3.29.2", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.11", + "dayjs": "^1.11.10", + "dompurify": "^3.2.1", + "katex": "^0.16.9", + "khroma": "^2.1.0", + "lodash-es": "^4.17.21", + "marked": "^13.0.2", + "roughjs": "^4.6.6", + "stylis": "^4.3.1", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.1" + } + }, + "node_modules/mermaid/node_modules/marked": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz", + "integrity": "sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==", + "optional": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mermaid/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", + "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "dev": true, + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", + "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", + "dev": true, + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mlly": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", + "optional": true, + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "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==" + }, + "node_modules/msgpackr": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.2.tgz", + "integrity": "sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==", + "dev": true, + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/ngx-markdown": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-18.1.0.tgz", + "integrity": "sha512-n4HFSm5oqVMXFuD+WXIVkI6NyxD8Oubr4B3c9U1J7Ptr6t9DVnkNBax3yxWc+8Wli+FXTuGEnDXzB3sp7E9paA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "clipboard": "^2.0.11", + "emoji-toolkit": ">= 8.0.0 < 10.0.0", + "katex": "^0.16.0", + "mermaid": ">= 10.6.0 < 12.0.0", + "prismjs": "^1.28.0" + }, + "peerDependencies": { + "@angular/common": "^18.0.0", + "@angular/core": "^18.0.0", + "@angular/platform-browser": "^18.0.0", + "marked": ">= 9.0.0 < 13.0.0", + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.14.0" + } + }, + "node_modules/nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "!win32" + ], + "dependencies": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node_modules/nice-napi/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.3.1.tgz", + "integrity": "sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", + "semver": "^7.3.5", + "tar": "^6.2.1", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", + "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-install-checks": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", + "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", + "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", + "dev": true, + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-packlist": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", + "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", + "dev": true, + "dependencies": { + "ignore-walk": "^6.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz", + "integrity": "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==", + "dev": true, + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", + "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", + "dev": true, + "dependencies": { + "@npmcli/redact": "^2.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "dev": true, + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/ordered-binary": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.3.tgz", + "integrity": "sha512-oGFr3T+pYdTGJ+YFEILMpS3es+GiIbs9h/XQrclBXUtd44ey7XwfsMzM31f64I1SQOawDoDr/D823kNCADI8TA==", + "dev": true + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", + "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/package-manager-detector": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.9.tgz", + "integrity": "sha512-+vYvA/Y31l8Zk8dwxHhL3JfTuHPm6tlxM2A3GeQyl7ovYnSp1+mzAxClxaOr0qO1TtPxbQxetI7v5XqKLJZk7Q==", + "optional": true + }, + "node_modules/pacote": { + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-18.0.6.tgz", + "integrity": "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==", + "dev": true, + "dependencies": { + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/package-json": "^5.1.0", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^8.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "devOptional": true, + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-7.0.0.tgz", + "integrity": "sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==", + "dev": true, + "dependencies": { + "entities": "^4.3.0", + "parse5": "^7.0.0", + "parse5-sax-parser": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", + "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==", + "dev": true, + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==" + }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "optional": true + }, + "node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pathe": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", + "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==", + "optional": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/piscina": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.6.1.tgz", + "integrity": "sha512-z30AwWGtQE+Apr+2WBZensP2lIvwoaMcOPkQlIEmSGMJNUvaYACylPYrQM6wSdUNJlnDVMSpLv7xTMJqlVshOA==", + "dev": true, + "optionalDependencies": { + "nice-napi": "^1.0.2" + } + }, + "node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "dev": true, + "dependencies": { + "find-up": "^6.3.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "optional": true, + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "optional": true + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "optional": true, + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, + "node_modules/postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "dev": true, + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-loader": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", + "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", + "dev": true, + "dependencies": { + "cosmiconfig": "^9.0.0", + "jiti": "^1.20.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/proc-log": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true, + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", + "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", + "dev": true + }, + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "dev": true, + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.0.tgz", + "integrity": "sha512-/Tvpny/RVVicqlYTKwt/GtpZRsPG1CmJNhxVKGz+Sy/4MONfXCVNK69MFgGKdUt0/324q3ClI2dICcPgISrC8g==", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "optional": true + }, + "node_modules/rollup": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", + "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.22.4", + "@rollup/rollup-android-arm64": "4.22.4", + "@rollup/rollup-darwin-arm64": "4.22.4", + "@rollup/rollup-darwin-x64": "4.22.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", + "@rollup/rollup-linux-arm-musleabihf": "4.22.4", + "@rollup/rollup-linux-arm64-gnu": "4.22.4", + "@rollup/rollup-linux-arm64-musl": "4.22.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", + "@rollup/rollup-linux-riscv64-gnu": "4.22.4", + "@rollup/rollup-linux-s390x-gnu": "4.22.4", + "@rollup/rollup-linux-x64-gnu": "4.22.4", + "@rollup/rollup-linux-x64-musl": "4.22.4", + "@rollup/rollup-win32-arm64-msvc": "4.22.4", + "@rollup/rollup-win32-ia32-msvc": "4.22.4", + "@rollup/rollup-win32-x64-msvc": "4.22.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "optional": true, + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "optional": true + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sass": { + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-loader": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.0.tgz", + "integrity": "sha512-n13Z+3rU9A177dk4888czcVFiC8CL9dii4qpXWUg3YIIgZEvi9TCFKjOQcbK0kJM7DJu9VucrZFddvNfYCPwtw==", + "dev": true, + "dependencies": { + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true, + "optional": true + }, + "node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==", + "optional": true + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sigstore": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.3.1.tgz", + "integrity": "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==", + "dev": true, + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dev": true, + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", + "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", + "dev": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + }, + "node_modules/ssri": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", + "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "dev": true, + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dev": true, + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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==", + "dev": true, + "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/string-width-cjs": { + "name": "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/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==", + "dev": true + }, + "node_modules/string-width-cjs/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/stylis": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.5.tgz", + "integrity": "sha512-K7npNOKGRYuhAFFzkzMGfxFDpN6gDwf8hcMiE+uveTVbBgm93HrNP3ZDUpKqzZ4pG7TP6fmb+EMAQPjq9FqqvA==", + "optional": true + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/terser": { + "version": "5.31.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", + "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", + "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/thingies": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", + "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", + "dev": true, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "tslib": "^2" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", + "optional": true + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "optional": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tree-dump": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz", + "integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "optional": true, + "engines": { + "node": ">=6.10" + } + }, + "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==" + }, + "node_modules/tuf-js": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", + "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", + "dev": true, + "dependencies": { + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.40.tgz", + "integrity": "sha512-us1E3K+3jJppDBa3Tl0L3MOJiGhe1C6P0+nIvQAFYbxlMAx0h81eOwLmU57xgqToduDDPx3y5QsdjPfDu+FgOQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "optional": true + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.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", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vite/node_modules/postcss": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "dev": true, + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "optional": true, + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "optional": true, + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "optional": true + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "optional": true + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "optional": true + }, + "node_modules/watchpack": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webpack": { + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", + "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^4.6.0", + "mime-types": "^2.1.31", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz", + "integrity": "sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.4.0", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "rimraf": "^5.0.5", + "schema-utils": "^4.2.0", + "selfsigned": "^2.4.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^7.1.0", + "ws": "^8.16.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-dev-server/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webpack-dev-server/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "dependencies": { + "typed-assert": "^1.0.8" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", + "webpack": "^5.12.0" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/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==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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==", + "dev": true + }, + "node_modules/wrap-ansi/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/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==" + }, + "node_modules/yargs/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", + "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", + "peerDependencies": { + "zod": "^3.24.1" + } + }, + "node_modules/zone.js": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz", + "integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==" + } + } +} diff --git a/samples/chatbot/genkit-app/package.json b/samples/js-chatbot/genkit-app/package.json similarity index 93% rename from samples/chatbot/genkit-app/package.json rename to samples/js-chatbot/genkit-app/package.json index 465379b0b..1dd0c3c26 100644 --- a/samples/chatbot/genkit-app/package.json +++ b/samples/js-chatbot/genkit-app/package.json @@ -1,6 +1,6 @@ { - "name": "genkit-app", - "version": "0.0.0", + "name": "genkit-chat-app", + "version": "0.1.0", "scripts": { "ng": "ng", "start": "ng serve", @@ -26,7 +26,7 @@ "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3", - "genkit": "^0.9.0-rc || ^0.9" + "genkit": "^1.0.0-rc.14" }, "devDependencies": { "@angular-devkit/build-angular": "^18.0.2", diff --git a/samples/chatbot/genkit-app/public/favicon.ico b/samples/js-chatbot/genkit-app/public/favicon.ico similarity index 100% rename from samples/chatbot/genkit-app/public/favicon.ico rename to samples/js-chatbot/genkit-app/public/favicon.ico diff --git a/samples/chatbot/genkit-app/src/app/app.component.html b/samples/js-chatbot/genkit-app/src/app/app.component.html similarity index 100% rename from samples/chatbot/genkit-app/src/app/app.component.html rename to samples/js-chatbot/genkit-app/src/app/app.component.html diff --git a/samples/chatbot/genkit-app/src/app/app.component.scss b/samples/js-chatbot/genkit-app/src/app/app.component.scss similarity index 100% rename from samples/chatbot/genkit-app/src/app/app.component.scss rename to samples/js-chatbot/genkit-app/src/app/app.component.scss diff --git a/samples/chatbot/genkit-app/src/app/app.component.spec.ts b/samples/js-chatbot/genkit-app/src/app/app.component.spec.ts similarity index 100% rename from samples/chatbot/genkit-app/src/app/app.component.spec.ts rename to samples/js-chatbot/genkit-app/src/app/app.component.spec.ts diff --git a/samples/chatbot/genkit-app/src/app/app.component.ts b/samples/js-chatbot/genkit-app/src/app/app.component.ts similarity index 100% rename from samples/chatbot/genkit-app/src/app/app.component.ts rename to samples/js-chatbot/genkit-app/src/app/app.component.ts diff --git a/samples/chatbot/genkit-app/src/app/app.config.ts b/samples/js-chatbot/genkit-app/src/app/app.config.ts similarity index 100% rename from samples/chatbot/genkit-app/src/app/app.config.ts rename to samples/js-chatbot/genkit-app/src/app/app.config.ts diff --git a/samples/chatbot/genkit-app/src/app/app.routes.ts b/samples/js-chatbot/genkit-app/src/app/app.routes.ts similarity index 100% rename from samples/chatbot/genkit-app/src/app/app.routes.ts rename to samples/js-chatbot/genkit-app/src/app/app.routes.ts diff --git a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.html b/samples/js-chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.html similarity index 100% rename from samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.html rename to samples/js-chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.html diff --git a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.scss b/samples/js-chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.scss similarity index 100% rename from samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.scss rename to samples/js-chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.scss diff --git a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts b/samples/js-chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts similarity index 100% rename from samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts rename to samples/js-chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.spec.ts diff --git a/samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts b/samples/js-chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts similarity index 100% rename from samples/chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts rename to samples/js-chatbot/genkit-app/src/app/samples/chatbot/chatbot.component.ts diff --git a/samples/chatbot/genkit-app/src/index.html b/samples/js-chatbot/genkit-app/src/index.html similarity index 100% rename from samples/chatbot/genkit-app/src/index.html rename to samples/js-chatbot/genkit-app/src/index.html diff --git a/samples/chatbot/genkit-app/src/main.ts b/samples/js-chatbot/genkit-app/src/main.ts similarity index 100% rename from samples/chatbot/genkit-app/src/main.ts rename to samples/js-chatbot/genkit-app/src/main.ts diff --git a/samples/chatbot/genkit-app/src/styles.scss b/samples/js-chatbot/genkit-app/src/styles.scss similarity index 100% rename from samples/chatbot/genkit-app/src/styles.scss rename to samples/js-chatbot/genkit-app/src/styles.scss diff --git a/samples/chatbot/genkit-app/tsconfig.app.json b/samples/js-chatbot/genkit-app/tsconfig.app.json similarity index 100% rename from samples/chatbot/genkit-app/tsconfig.app.json rename to samples/js-chatbot/genkit-app/tsconfig.app.json diff --git a/samples/chatbot/genkit-app/tsconfig.json b/samples/js-chatbot/genkit-app/tsconfig.json similarity index 100% rename from samples/chatbot/genkit-app/tsconfig.json rename to samples/js-chatbot/genkit-app/tsconfig.json diff --git a/samples/chatbot/genkit-app/tsconfig.spec.json b/samples/js-chatbot/genkit-app/tsconfig.spec.json similarity index 100% rename from samples/chatbot/genkit-app/tsconfig.spec.json rename to samples/js-chatbot/genkit-app/tsconfig.spec.json diff --git a/samples/js-chatbot/package-lock.json b/samples/js-chatbot/package-lock.json new file mode 100644 index 000000000..f5109a5c8 --- /dev/null +++ b/samples/js-chatbot/package-lock.json @@ -0,0 +1,5378 @@ +{ + "name": "js-angular", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "js-angular", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "concurrently": "^8.2.2", + "genkit-cli": "^1.0.0-rc.14" + } + }, + "node_modules/@asteasolutions/zod-to-openapi": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.0.tgz", + "integrity": "sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==", + "dev": true, + "dependencies": { + "openapi3-ts": "^4.1.2" + }, + "peerDependencies": { + "zod": "^3.20.2" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", + "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@genkit-ai/telemetry-server": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.14.tgz", + "integrity": "sha512-cFxLS8IkXhRds9+wgaru3QirgehQImG9CuhLfjo09MRcIM21Vr9F083YVnlO8WDckWz5NwclkfFKkD25FDpE/w==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@genkit-ai/tools-common": "1.0.0-rc.14", + "@google-cloud/firestore": "^7.6.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "async-mutex": "^0.5.0", + "express": "^4.21.0", + "zod": "^3.22.4" + } + }, + "node_modules/@genkit-ai/tools-common": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.14.tgz", + "integrity": "sha512-50PE/CIL3u9YC4c3vifz6CVSlC1cmQ3xAHMOsUxsRRvcPzIbL8jOCYMKAnbdgAkp0WGgWG+fLjiKbUPZydEgcg==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@trpc/server": "10.45.0", + "adm-zip": "^0.5.12", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "axios": "^1.7.7", + "body-parser": "^1.20.2", + "chokidar": "^3.5.3", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "configstore": "^5.0.1", + "express": "^4.21.0", + "get-port": "5.1.1", + "glob": "^10.3.12", + "inquirer": "^8.2.0", + "js-yaml": "^4.1.0", + "json-2-csv": "^5.5.1", + "json-schema": "^0.4.0", + "terminate": "^2.6.1", + "tsx": "^4.19.2", + "uuid": "^9.0.1", + "winston": "^3.11.0", + "yaml": "^2.4.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@google-cloud/firestore": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.0.tgz", + "integrity": "sha512-88uZ+jLsp1aVMj7gh3EKYH1aulTAMFAp8sH/v5a9w8q8iqSG27RiWLoxSAFr/XocZ9hGiWH1kEnBw+zl3xAgNA==", + "dev": true, + "dependencies": { + "@opentelemetry/api": "^1.3.0", + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.3.3", + "protobufjs": "^7.2.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz", + "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==", + "dev": true, + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dev": true, + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz", + "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==", + "dev": true, + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "dev": true, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz", + "integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==", + "dev": true, + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", + "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz", + "integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz", + "integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dev": true, + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz", + "integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==", + "dev": true, + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", + "dev": true, + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", + "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", + "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", + "dev": true, + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz", + "integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==", + "dev": true, + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", + "@opentelemetry/exporter-zipkin": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/sdk-trace-node": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", + "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==", + "dev": true, + "dependencies": { + "@opentelemetry/context-async-hooks": "1.25.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/propagator-b3": "1.25.1", + "@opentelemetry/propagator-jaeger": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "dev": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dev": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "dev": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "dev": true + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trpc/server": { + "version": "10.45.0", + "resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.45.0.tgz", + "integrity": "sha512-2Fwzv6nqpE0Ie/G7PeS0EVR89zLm+c1Mw7T+RAGtU807j4oaUx0zGkBXTu5u9AI+j+BYNN2GZxJcuDTAecbr1A==", + "dev": true, + "funding": [ + "https://trpc.io/sponsor" + ] + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", + "dev": true + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", + "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", + "dev": true, + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "dev": true, + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==", + "dev": true + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "dev": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "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==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dev": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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==", + "dev": true, + "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==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/concurrently": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deeks": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/deeks/-/deeks-3.1.0.tgz", + "integrity": "sha512-e7oWH1LzIdv/prMQ7pmlDlaVoL64glqzvNgkgQNgyec9ORPHrT2jaOqMtRyqJuwWjtfb6v+2rk9pmaHj+F137A==", + "dev": true, + "engines": { + "node": ">= 16" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/doc-path": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-4.1.1.tgz", + "integrity": "sha512-h1ErTglQAVv2gCnOpD3sFS6uolDbOKHDU1BZq+Kl3npPqroU3dYL42lUgMfd5UimlwtRgp7C9dLGwqQ5D2HYgQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "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==", + "dev": true + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/extract-zip/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==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dev": true, + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "dev": true, + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/genkit-cli": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.14.tgz", + "integrity": "sha512-JR8Vvm2rGGQMsBkq+dWSypUC4ij1Uew5cjFaS74flyv/glV6RMNMuXP5XQBc5C9zIkfXeT+kng8BghHM4Gx/4g==", + "dev": true, + "dependencies": { + "@genkit-ai/telemetry-server": "1.0.0-rc.14", + "@genkit-ai/tools-common": "1.0.0-rc.14", + "axios": "^1.7.7", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "extract-zip": "^2.0.1", + "get-port": "5.1.1", + "inquirer": "^8.2.0", + "open": "^6.3.0", + "ora": "^5.4.1" + }, + "bin": { + "genkit": "dist/bin/genkit.js" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "dev": true, + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", + "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", + "dev": true, + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dev": true, + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/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==", + "dev": true + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/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==", + "dev": true + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/import-in-the-middle": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.12.0.tgz", + "integrity": "sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w==", + "dev": true, + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-2-csv": { + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.8.tgz", + "integrity": "sha512-eMQHOwV+av8Sgo+fkbEbQWOw/kwh89AZ5fNA8TYfcooG6TG1ZOL2WcPUrngIMIK8dBJitQ8QEU0zbncQ0CX4CQ==", + "dev": true, + "dependencies": { + "deeks": "3.1.0", + "doc-path": "4.1.1" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dev": true, + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dev": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dev": true, + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dev": true, + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/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==", + "dev": true + }, + "node_modules/long": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==", + "dev": true + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openapi3-ts": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.4.0.tgz", + "integrity": "sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==", + "dev": true, + "dependencies": { + "yaml": "^2.5.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "dev": true, + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.0.tgz", + "integrity": "sha512-/Tvpny/RVVicqlYTKwt/GtpZRsPG1CmJNhxVKGz+Sy/4MONfXCVNK69MFgGKdUt0/324q3ClI2dICcPgISrC8g==", + "dev": true, + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/require-in-the-middle/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/require-in-the-middle/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==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "dev": true, + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/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==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", + "dev": true + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "dev": true, + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==", + "dev": true + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/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==", + "dev": true + }, + "node_modules/terminate": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/terminate/-/terminate-2.8.0.tgz", + "integrity": "sha512-bcbjJEg0wY5nuJXvGxxHfmoEPkyHLCctUKO6suwtxy7jVSgGcgPeGwpbLDLELFhIaxCGRr3dPvyNg1yuz2V0eg==", + "dev": true, + "dependencies": { + "ps-tree": "^1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true, + "engines": { + "node": ">= 14.0.0" + } + }, + "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==", + "dev": true + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "dev": true, + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dev": true, + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", + "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", + "dev": true, + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} diff --git a/samples/chatbot/package.json b/samples/js-chatbot/package.json similarity index 88% rename from samples/chatbot/package.json rename to samples/js-chatbot/package.json index 5cfb52ff9..db5de649c 100644 --- a/samples/chatbot/package.json +++ b/samples/js-chatbot/package.json @@ -12,6 +12,7 @@ "author": "", "license": "ISC", "devDependencies": { - "concurrently": "^8.2.2" + "concurrently": "^8.2.2", + "genkit-cli": "^1.0.0-rc.14" } } diff --git a/samples/js-chatbot/server/package-lock.json b/samples/js-chatbot/server/package-lock.json new file mode 100644 index 000000000..574af6e28 --- /dev/null +++ b/samples/js-chatbot/server/package-lock.json @@ -0,0 +1,6375 @@ +{ + "name": "js-angular", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "js-angular", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@genkit-ai/express": "^1.0.0-rc.14", + "@genkit-ai/vertexai": "^1.0.0-rc.14", + "express": "^4.21.0", + "genkit": "^1.0.0-rc.14", + "partial-json": "^0.1.7", + "zod": "^3.23.8" + }, + "devDependencies": { + "genkit-cli": "^1.0.0-rc.14", + "tsx": "^4.19.2", + "typescript": "^5.4.5" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.24.3", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.24.3.tgz", + "integrity": "sha512-916wJXO6T6k8R6BAAcLhLPv/pnLGy7YSEBZXZ1XTFbLcTZE8oTy3oDW9WJf9KKZwMvVcePIfoTSvzXHRcGxkQQ==", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7", + "web-streams-polyfill": "^3.2.1" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@anthropic-ai/vertex-sdk": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@anthropic-ai/vertex-sdk/-/vertex-sdk-0.4.3.tgz", + "integrity": "sha512-2Uef0C5P2Hx+T88RnUSRA3u4aZqmqnrRSOb2N64ozgKPiSUPTM5JlggAq2b32yWMj5d3MLYa6spJXKMmHXOcoA==", + "dependencies": { + "@anthropic-ai/sdk": ">=0.14 <1", + "google-auth-library": "^9.4.2" + } + }, + "node_modules/@asteasolutions/zod-to-openapi": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.0.tgz", + "integrity": "sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==", + "dev": true, + "dependencies": { + "openapi3-ts": "^4.1.2" + }, + "peerDependencies": { + "zod": "^3.20.2" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@fastify/busboy": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", + "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==", + "optional": true + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==", + "optional": true + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", + "optional": true + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==", + "optional": true + }, + "node_modules/@firebase/component": { + "version": "0.6.12", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.12.tgz", + "integrity": "sha512-YnxqjtohLbnb7raXt2YuA44cC1wA9GiehM/cmxrsoxKlFxBLy2V0OkRSj9gpngAE0UoJ421Wlav9ycO7lTPAUw==", + "optional": true, + "dependencies": { + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.11.tgz", + "integrity": "sha512-gLrw/XeioswWUXgpVKCPAzzoOuvYNqK5fRUeiJTzO7Mlp9P6ylFEyPJlRBl1djqYye641r3MX6AmIeMXwjgwuQ==", + "optional": true, + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.2.tgz", + "integrity": "sha512-5zvdnMsfDHvrQAVM6jBS7CkBpu+z3YbpFdhxRsrK1FP45IEfxlzpeuEUb17D/tpM10vfq4Ok0x5akIBaCv7gfA==", + "optional": true, + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/database": "1.0.11", + "@firebase/database-types": "1.0.8", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.8.tgz", + "integrity": "sha512-6lPWIGeufhUq1heofZULyVvWFhD01TUrkkB9vyhmksjZ4XF7NaivQp9rICMk7QNhqwa+uDCaj4j+Q8qqcSVZ9g==", + "optional": true, + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.10.3" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", + "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "optional": true, + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/util": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.3.tgz", + "integrity": "sha512-wfoF5LTy0m2ufUapV0ZnpcGQvuavTbJ5Qr1Ze9OJGL70cSMvhDyjS4w2121XdA3lGZSTOsDOyGhpoDtYwck85A==", + "optional": true, + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@genkit-ai/ai": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.14.tgz", + "integrity": "sha512-5CHyRixumiM5uHkzwHl7IWbrYSzWvhDvYAsMoq9mmI8iCJA9g/3u8JOcHSRiseuaap5gv/QGm809IpIlak55ZA==", + "dependencies": { + "@genkit-ai/core": "1.0.0-rc.14", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.11.19", + "colorette": "^2.0.20", + "dotprompt": "^1.0.0-dev.3 || ^1", + "json5": "^2.2.3", + "node-fetch": "^3.3.2", + "partial-json": "^0.1.7", + "uuid": "^10.0.0" + } + }, + "node_modules/@genkit-ai/ai/node_modules/@types/node": { + "version": "20.17.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", + "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@genkit-ai/ai/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/@genkit-ai/ai/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@genkit-ai/core": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.14.tgz", + "integrity": "sha512-8h0Vl3SCP0vYIN60uELaZWEAP6BjLxXTcoWv2MFn7khxblzVkMWq98IxPThHo3s5BGB+Pxnhnsywdb/ItliFUA==", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "@types/json-schema": "^7.0.15", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "async-mutex": "^0.5.0", + "body-parser": "^1.20.3", + "cors": "^2.8.5", + "dotprompt": "^1.0.0-dev.3 || ^1", + "express": "^4.21.0", + "get-port": "^5.1.0", + "json-schema": "^0.4.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/express": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/express/-/express-1.0.0-rc.14.tgz", + "integrity": "sha512-I8VcuV2iyJXs/X3nCQZ/UR+FIO7XwaBo6+KWSDnugJrCweQZQ0LCYHbinSp1obLm/xCML7CWnqpZTX87mnbe3w==", + "dependencies": { + "body-parser": "^1.20.3", + "cors": "^2.8.5" + }, + "peerDependencies": { + "express": "^4.21.1", + "genkit": "^1.0.0-rc.14" + } + }, + "node_modules/@genkit-ai/telemetry-server": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.14.tgz", + "integrity": "sha512-cFxLS8IkXhRds9+wgaru3QirgehQImG9CuhLfjo09MRcIM21Vr9F083YVnlO8WDckWz5NwclkfFKkD25FDpE/w==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@genkit-ai/tools-common": "1.0.0-rc.14", + "@google-cloud/firestore": "^7.6.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "async-mutex": "^0.5.0", + "express": "^4.21.0", + "zod": "^3.22.4" + } + }, + "node_modules/@genkit-ai/tools-common": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.14.tgz", + "integrity": "sha512-50PE/CIL3u9YC4c3vifz6CVSlC1cmQ3xAHMOsUxsRRvcPzIbL8jOCYMKAnbdgAkp0WGgWG+fLjiKbUPZydEgcg==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@trpc/server": "10.45.0", + "adm-zip": "^0.5.12", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "axios": "^1.7.7", + "body-parser": "^1.20.2", + "chokidar": "^3.5.3", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "configstore": "^5.0.1", + "express": "^4.21.0", + "get-port": "5.1.1", + "glob": "^10.3.12", + "inquirer": "^8.2.0", + "js-yaml": "^4.1.0", + "json-2-csv": "^5.5.1", + "json-schema": "^0.4.0", + "terminate": "^2.6.1", + "tsx": "^4.19.2", + "uuid": "^9.0.1", + "winston": "^3.11.0", + "yaml": "^2.4.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/vertexai": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/vertexai/-/vertexai-1.0.0-rc.14.tgz", + "integrity": "sha512-7j3xV9wdqIhjUjvy6cogIaTATl28z+wjiP9hLfeYb6XOyG7HFve5jZ4xzDHK2pGVOjblrP2XDyahOE2BUK6fyQ==", + "dependencies": { + "@anthropic-ai/sdk": "^0.24.3", + "@anthropic-ai/vertex-sdk": "^0.4.0", + "@google-cloud/aiplatform": "^3.23.0", + "@google-cloud/vertexai": "^1.9.2", + "@mistralai/mistralai-gcp": "^1.3.5", + "google-auth-library": "^9.14.2", + "googleapis": "^140.0.1", + "node-fetch": "^3.3.2", + "openai": "^4.52.7" + }, + "optionalDependencies": { + "@google-cloud/bigquery": "^7.8.0", + "firebase-admin": ">=12.2" + }, + "peerDependencies": { + "genkit": "^1.0.0-rc.14" + } + }, + "node_modules/@google-cloud/aiplatform": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/@google-cloud/aiplatform/-/aiplatform-3.34.0.tgz", + "integrity": "sha512-Ii1CXJ59g5hcVYNZOx08XBV5nq0JIOSo2I9uC/WYkdXWekc3XSV9emRz8pKOQSULzrTOTnD80N4re49S07xfyQ==", + "dependencies": { + "google-gax": "^4.0.3", + "protobuf.js": "^1.1.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/bigquery": { + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@google-cloud/bigquery/-/bigquery-7.9.1.tgz", + "integrity": "sha512-ZkcRMpBoFLxIh6TiQBywA22yT3c2j0f07AHWEMjtYqMQzZQbFrpxuJU2COp3tyjZ91ZIGHe4gY7/dGZL88cltg==", + "optional": true, + "dependencies": { + "@google-cloud/common": "^5.0.0", + "@google-cloud/paginator": "^5.0.2", + "@google-cloud/precise-date": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "arrify": "^2.0.1", + "big.js": "^6.0.0", + "duplexify": "^4.0.0", + "extend": "^3.0.2", + "is": "^3.3.0", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/common": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-5.0.2.tgz", + "integrity": "sha512-V7bmBKYQyu0eVG2BFejuUjlBt+zrya6vtsKdY+JxMM/dNntPF41vZ9+LhOshEUH01zOHEqBSvI7Dad7ZS6aUeA==", + "optional": true, + "dependencies": { + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "arrify": "^2.0.1", + "duplexify": "^4.1.1", + "extend": "^3.0.2", + "google-auth-library": "^9.0.0", + "html-entities": "^2.5.2", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/firestore": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.0.tgz", + "integrity": "sha512-88uZ+jLsp1aVMj7gh3EKYH1aulTAMFAp8sH/v5a9w8q8iqSG27RiWLoxSAFr/XocZ9hGiWH1kEnBw+zl3xAgNA==", + "devOptional": true, + "dependencies": { + "@opentelemetry/api": "^1.3.0", + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.3.3", + "protobufjs": "^7.2.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/paginator": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", + "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", + "optional": true, + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/precise-date": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-4.0.0.tgz", + "integrity": "sha512-1TUx3KdaU3cN7nfCdNf+UVqA/PSX29Cjcox3fZZBtINlRrXVTmUkQnCKv2MbBUbCopbK4olAT1IHl76uZyCiVA==", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/projectify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", + "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/promisify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", + "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google-cloud/storage": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.15.0.tgz", + "integrity": "sha512-/j/+8DFuEOo33fbdX0V5wjooOoFahEaMEdImHBmM2tH9MPHJYNtmXOf2sGUmZmiufSukmBEvdlzYgDkkgeBiVQ==", + "optional": true, + "dependencies": { + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "abort-controller": "^3.0.0", + "async-retry": "^1.3.3", + "duplexify": "^4.1.3", + "fast-xml-parser": "^4.4.1", + "gaxios": "^6.0.2", + "google-auth-library": "^9.6.3", + "html-entities": "^2.5.2", + "mime": "^3.0.0", + "p-limit": "^3.0.1", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google-cloud/storage/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@google-cloud/vertexai": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@google-cloud/vertexai/-/vertexai-1.9.3.tgz", + "integrity": "sha512-35o5tIEMLW3JeFJOaaMNR2e5sq+6rpnhrF97PuAxeOm0GlqVTESKhkGj7a5B5mmJSSSU3hUfIhcQCRRsw4Ipzg==", + "dependencies": { + "google-auth-library": "^9.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz", + "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@mistralai/mistralai-gcp": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai-gcp/-/mistralai-gcp-1.4.0.tgz", + "integrity": "sha512-QYBbR/T1U4qZ88m6l5RpOlhly2mXWsXi0owicSX6zt6pBaMORxRs6ZRLmaYX5BNjGqxQUl+LtsSwpMtXW2uU2A==", + "dependencies": { + "google-auth-library": "^9.11.0" + }, + "peerDependencies": { + "zod": ">= 3" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz", + "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz", + "integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", + "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz", + "integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz", + "integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz", + "integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", + "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", + "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz", + "integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", + "@opentelemetry/exporter-zipkin": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/sdk-trace-node": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", + "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.25.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/propagator-b3": "1.25.1", + "@opentelemetry/propagator-jaeger": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trpc/server": { + "version": "10.45.0", + "resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.45.0.tgz", + "integrity": "sha512-2Fwzv6nqpE0Ie/G7PeS0EVR89zLm+c1Mw7T+RAGtU807j4oaUx0zGkBXTu5u9AI+j+BYNN2GZxJcuDTAecbr1A==", + "dev": true, + "funding": [ + "https://trpc.io/sponsor" + ] + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "optional": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==" + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "optional": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "optional": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "optional": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.8.tgz", + "integrity": "sha512-7fx54m60nLFUVYlxAB1xpe9CBWX2vSrk50Y6ogRJ1v5xxtba7qXTg5BgYDN5dq+yuQQ9HaVlHJyAAt1/mxryFg==", + "optional": true, + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "optional": true + }, + "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==", + "optional": true + }, + "node_modules/@types/node": { + "version": "18.19.74", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", + "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "optional": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "optional": true + }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "optional": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "optional": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "dev": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "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==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "optional": true, + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/big.js": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.2.tgz", + "integrity": "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==", + "optional": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bigjs" + } + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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==", + "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==" + }, + "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==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deeks": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/deeks/-/deeks-3.1.0.tgz", + "integrity": "sha512-e7oWH1LzIdv/prMQ7pmlDlaVoL64glqzvNgkgQNgyec9ORPHrT2jaOqMtRyqJuwWjtfb6v+2rk9pmaHj+F137A==", + "dev": true, + "engines": { + "node": ">= 16" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/doc-path": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-4.1.1.tgz", + "integrity": "sha512-h1ErTglQAVv2gCnOpD3sFS6uolDbOKHDU1BZq+Kl3npPqroU3dYL42lUgMfd5UimlwtRgp7C9dLGwqQ5D2HYgQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotprompt": { + "version": "1.0.0-dev.3", + "resolved": "https://registry.npmjs.org/dotprompt/-/dotprompt-1.0.0-dev.3.tgz", + "integrity": "sha512-gAcIhG+vzZlrZcBN/lO8gEMUtXHKMLbs1+snJuZrPkZnNgnWrzz1hYN4vYotpng/5fHsYSPUEckECy6qUNSrsQ==", + "dependencies": { + "handlebars": "^4.7.8", + "yaml": "^2.5.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "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==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/extract-zip/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==", + "dev": true + }, + "node_modules/farmhash-modern": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", + "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==", + "optional": true, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fast-xml-parser": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz", + "integrity": "sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "optional": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/firebase-admin": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.0.2.tgz", + "integrity": "sha512-YWVpoN+tZVSRXF0qC0gojoF5bSqvBRbnBk8+xUtFiguM2L4vB7f0moAwV1VVWDDHvTnvQ68OyTMpdp6wKo/clw==", + "optional": true, + "dependencies": { + "@fastify/busboy": "^3.0.0", + "@firebase/database-compat": "^2.0.0", + "@firebase/database-types": "^1.0.6", + "@types/node": "^22.8.7", + "farmhash-modern": "^1.1.0", + "google-auth-library": "^9.14.2", + "jsonwebtoken": "^9.0.0", + "jwks-rsa": "^3.1.0", + "node-forge": "^1.3.1", + "uuid": "^11.0.2" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@google-cloud/firestore": "^7.11.0", + "@google-cloud/storage": "^7.14.0" + } + }, + "node_modules/firebase-admin/node_modules/@types/node": { + "version": "22.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", + "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", + "optional": true, + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/firebase-admin/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "optional": true + }, + "node_modules/firebase-admin/node_modules/uuid": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "optional": true, + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formdata-node/node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "devOptional": true + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/genkit": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.14.tgz", + "integrity": "sha512-JB6fTcJ5CI4UhsP4uNyXpi9/OatyhPwOkBXnbHafE5rdibXuKUXadbb2LC+PWfG3+7WrLXGKlrj7kOIN3Hq2lw==", + "dependencies": { + "@genkit-ai/ai": "1.0.0-rc.14", + "@genkit-ai/core": "1.0.0-rc.14", + "uuid": "^10.0.0" + } + }, + "node_modules/genkit-cli": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.14.tgz", + "integrity": "sha512-JR8Vvm2rGGQMsBkq+dWSypUC4ij1Uew5cjFaS74flyv/glV6RMNMuXP5XQBc5C9zIkfXeT+kng8BghHM4Gx/4g==", + "dev": true, + "dependencies": { + "@genkit-ai/telemetry-server": "1.0.0-rc.14", + "@genkit-ai/tools-common": "1.0.0-rc.14", + "axios": "^1.7.7", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "extract-zip": "^2.0.1", + "get-port": "5.1.1", + "inquirer": "^8.2.0", + "open": "^6.3.0", + "ora": "^5.4.1" + }, + "bin": { + "genkit": "dist/bin/genkit.js" + } + }, + "node_modules/genkit/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", + "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/googleapis": { + "version": "140.0.1", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-140.0.1.tgz", + "integrity": "sha512-ZGvBX4mQcFXO9ACnVNg6Aqy3KtBPB5zTuue43YVLxwn8HSv8jB7w+uDKoIPSoWuxGROgnj2kbng6acXncOQRNA==", + "dependencies": { + "google-auth-library": "^9.0.0", + "googleapis-common": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis-common": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.2.0.tgz", + "integrity": "sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==", + "dependencies": { + "extend": "^3.0.2", + "gaxios": "^6.0.3", + "google-auth-library": "^9.7.0", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "optional": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz", + "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==", + "optional": true + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/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==" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/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==" + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/import-in-the-middle": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.12.0.tgz", + "integrity": "sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-2-csv": { + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.8.tgz", + "integrity": "sha512-eMQHOwV+av8Sgo+fkbEbQWOw/kwh89AZ5fNA8TYfcooG6TG1ZOL2WcPUrngIMIK8dBJitQ8QEU0zbncQ0CX4CQ==", + "dev": true, + "dependencies": { + "deeks": "3.1.0", + "doc-path": "4.1.1" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "optional": true, + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "optional": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "optional": true, + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/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==", + "optional": true + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", + "optional": true, + "dependencies": { + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", + "debug": "^4.3.4", + "jose": "^4.14.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jwks-rsa/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/jwks-rsa/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==", + "optional": true + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", + "optional": true + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "optional": true + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "optional": true + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "optional": true + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "optional": true + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "optional": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "optional": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "optional": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "optional": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dev": true, + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/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==", + "dev": true + }, + "node_modules/long": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-memoizer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", + "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", + "optional": true, + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "6.0.0" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "optional": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openai": { + "version": "4.82.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.82.0.tgz", + "integrity": "sha512-1bTxOVGZuVGsKKUWbh3BEwX1QxIXUftJv+9COhhGGVDTFwiaOd4gWsMynF2ewj1mg6by3/O+U8+EEHpWRdPaJg==", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/openapi3-ts": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.4.0.tgz", + "integrity": "sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==", + "dev": true, + "dependencies": { + "yaml": "^2.5.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "optional": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobuf.js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/protobuf.js/-/protobuf.js-1.1.2.tgz", + "integrity": "sha512-USO7Xus/pzPw549M1TguiyoOrKEhm9VMXv+CkDufcjMC8Rd7EPbxeRQPEjCV8ua1tm0k7z9xHkogcxovZogWdA==", + "dependencies": { + "long": "~1.1.2" + } + }, + "node_modules/protobuf.js/node_modules/long": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/long/-/long-1.1.5.tgz", + "integrity": "sha512-TU6nAF5SdasnTr28c7e74P4Crbn9o3/zwo1pM22Wvg2i2vlZ4Eelxwu4QT7j21z0sDBlJDEnEZjXTZg2J8WJrg==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.0.tgz", + "integrity": "sha512-/Tvpny/RVVicqlYTKwt/GtpZRsPG1CmJNhxVKGz+Sy/4MONfXCVNK69MFgGKdUt0/324q3ClI2dICcPgISrC8g==", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/require-in-the-middle/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/require-in-the-middle/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==" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/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==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "optional": true + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/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==" + }, + "node_modules/teeny-request/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/terminate": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/terminate/-/terminate-2.8.0.tgz", + "integrity": "sha512-bcbjJEg0wY5nuJXvGxxHfmoEPkyHLCctUKO6suwtxy7jVSgGcgPeGwpbLDLELFhIaxCGRr3dPvyNg1yuz2V0eg==", + "dev": true, + "dependencies": { + "ps-tree": "^1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true, + "engines": { + "node": ">= 14.0.0" + } + }, + "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==" + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" + }, + "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==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "optional": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "optional": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "dev": true, + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dev": true, + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "optional": true + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", + "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} diff --git a/samples/chatbot/server/package.json b/samples/js-chatbot/server/package.json similarity index 81% rename from samples/chatbot/server/package.json rename to samples/js-chatbot/server/package.json index 4f13d4997..2dafe7673 100644 --- a/samples/chatbot/server/package.json +++ b/samples/js-chatbot/server/package.json @@ -15,14 +15,15 @@ "author": "", "license": "ISC", "dependencies": { - "genkit": "^0.9.0-rc || ^0.9", - "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9", + "genkit": "^1.0.0-rc.14", + "@genkit-ai/vertexai": "^1.0.0-rc.14", + "@genkit-ai/express": "^1.0.0-rc.14", "express": "^4.21.0", "partial-json": "^0.1.7", "zod": "^3.23.8" }, "devDependencies": { - "genkit-cli": "^0.9.0-rc || ^0.9", + "genkit-cli": "^1.0.0-rc.14", "typescript": "^5.4.5", "tsx": "^4.19.2" } diff --git a/samples/chatbot/server/src/index.ts b/samples/js-chatbot/server/src/index.ts similarity index 91% rename from samples/chatbot/server/src/index.ts rename to samples/js-chatbot/server/src/index.ts index 7bd2144bf..283b368a2 100644 --- a/samples/chatbot/server/src/index.ts +++ b/samples/js-chatbot/server/src/index.ts @@ -14,13 +14,14 @@ * limitations under the License. */ +import { startFlowServer } from '@genkit-ai/express'; import { gemini15Flash, vertexAI } from '@genkit-ai/vertexai'; import { VertexAIEvaluationMetricType, vertexAIEvaluation, } from '@genkit-ai/vertexai/evaluation'; import { llama31, vertexAIModelGarden } from '@genkit-ai/vertexai/modelgarden'; -import { ModelReference, PartSchema, genkit, run } from 'genkit'; +import { ModelReference, PartSchema, genkit } from 'genkit'; import { GenerateResponseChunkSchema } from 'genkit/model'; import { z } from 'zod'; import { inMemoryStore } from './memory.js'; @@ -55,7 +56,7 @@ const llms: ModelReference[] = [gemini15Flash, llama31]; const historyStore = inMemoryStore(); -export const chatbotFlow = ai.defineStreamingFlow( +export const chatbotFlow = ai.defineFlow( { name: 'chatbotFlow', inputSchema: AgentInput, @@ -64,7 +65,7 @@ export const chatbotFlow = ai.defineStreamingFlow( }, async (request, streamingCallback) => { // Retrieve conversation history. - const history = await run( + const history = await ai.run( 'retrieve-history', request.conversationId, async () => { @@ -81,7 +82,7 @@ export const chatbotFlow = ai.defineStreamingFlow( }); // Save history. - await run( + await ai.run( 'save-history', { conversationId: request.conversationId, @@ -95,6 +96,6 @@ export const chatbotFlow = ai.defineStreamingFlow( } ); -ai.startFlowServer({ +startFlowServer({ flows: [chatbotFlow], }); diff --git a/samples/chatbot/server/src/memory.ts b/samples/js-chatbot/server/src/memory.ts similarity index 100% rename from samples/chatbot/server/src/memory.ts rename to samples/js-chatbot/server/src/memory.ts diff --git a/samples/chatbot/server/tsconfig.json b/samples/js-chatbot/server/tsconfig.json similarity index 100% rename from samples/chatbot/server/tsconfig.json rename to samples/js-chatbot/server/tsconfig.json From 413b392ee9fed287bd75fd6218215551c752043a Mon Sep 17 00:00:00 2001 From: Michael Doyle Date: Mon, 3 Feb 2025 15:15:53 -0500 Subject: [PATCH 498/562] fix: improve erroneous ts doc for GenerateResponse.messages (#1801) --- js/ai/src/generate/response.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/js/ai/src/generate/response.ts b/js/ai/src/generate/response.ts index 92ff2e1e6..c03970fd7 100644 --- a/js/ai/src/generate/response.ts +++ b/js/ai/src/generate/response.ts @@ -167,10 +167,9 @@ export class GenerateResponse implements ModelResponseData { } /** - * Appends the message generated by the selected candidate to the messages already - * present in the generation request. The result of this method can be safely - * serialized to JSON for persistence in a database. - * @param index The candidate index to utilize during conversion, defaults to first candidate. + * Returns the message history for the request by concatenating the model + * response to the list of messages from the request. The result of this + * method can be safely serialized to JSON for persistence in a database. * @returns A serializable list of messages compatible with `generate({history})`. */ get messages(): MessageData[] { From 8ab6e906fb292c027a7bd1b9160a3f74a1ceb078 Mon Sep 17 00:00:00 2001 From: Hugo Aguirre Date: Mon, 3 Feb 2025 14:22:34 -0600 Subject: [PATCH 499/562] chore: update menu sample to 1.0 (#1804) --- samples/js-menu/package.json | 12 ++++++------ samples/js-menu/src/03/flows.ts | 3 +-- samples/js-menu/src/types.ts | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/samples/js-menu/package.json b/samples/js-menu/package.json index 1488af432..008662c95 100644 --- a/samples/js-menu/package.json +++ b/samples/js-menu/package.json @@ -16,14 +16,14 @@ "author": "", "license": "ISC", "dependencies": { - "genkit": "^0.9.0-rc || ^0.9", - "@genkit-ai/dev-local-vectorstore": "^0.9.0-rc || ^0.9", - "@genkit-ai/firebase": "^0.9.0-rc || ^0.9", - "@genkit-ai/evaluator": "^0.9.0-rc || ^0.9", - "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9" + "@genkit-ai/dev-local-vectorstore": "^1.0.0-rc.12", + "@genkit-ai/evaluator": "^1.0.0-rc.12", + "@genkit-ai/firebase": "^1.0.0-rc.12", + "@genkit-ai/vertexai": "^1.0.0-rc.12", + "genkit": "^1.0.0-rc.12" }, "devDependencies": { - "genkit-cli": "^0.9.0-rc || ^0.9", + "genkit-cli": "^1.0.0-rc.12", "rimraf": "^6.0.1", "typescript": "^5.3.3" } diff --git a/samples/js-menu/src/03/flows.ts b/samples/js-menu/src/03/flows.ts index f71bc3a98..2435e964f 100644 --- a/samples/js-menu/src/03/flows.ts +++ b/samples/js-menu/src/03/flows.ts @@ -16,7 +16,6 @@ import { MessageData } from '@genkit-ai/ai/model'; import { gemini15Flash } from '@genkit-ai/vertexai'; -import { run } from 'genkit'; import { ai } from '../genkit.js'; import { MenuItem } from '../types'; import { @@ -72,7 +71,7 @@ export const s03_multiTurnChatFlow = ai.defineFlow( // First fetch the chat history. We'll wrap this in a run block. // If we were going to a database for the history, // we might want to have that db result captured in the trace. - let history = await run('fetchHistory', async () => + let history = await ai.run('fetchHistory', async () => chatHistoryStore.read(input.sessionId) ); diff --git a/samples/js-menu/src/types.ts b/samples/js-menu/src/types.ts index efe7bbc99..145fc44e7 100644 --- a/samples/js-menu/src/types.ts +++ b/samples/js-menu/src/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import * as z from 'zod'; +import { z } from 'genkit'; // The data model for a restaurant menu From df5ce3142b8cc54c12d3e115211f61b74d1e224b Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 3 Feb 2025 15:25:19 -0500 Subject: [PATCH 500/562] chore(js/client): moved client to beta namespace (#1782) --- js/genkit/package.json | 4 ++-- js/genkit/src/client/client.ts | 2 +- js/plugins/express/README.md | 4 ++-- js/plugins/express/tests/express_test.ts | 2 +- js/plugins/firebase/tests/functions_test.ts | 2 +- js/plugins/next/README.md | 2 +- js/plugins/next/src/client.ts | 2 +- tests/src/flow_server_test.ts | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index a009cdd49..bfde4897a 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -151,7 +151,7 @@ "import": "./lib/plugin.mjs", "default": "./lib/plugin.js" }, - "./client": { + "./beta/client": { "types": "./lib/client/index.d.ts", "require": "./lib/client/index.js", "import": "./lib/client/index.mjs", @@ -214,7 +214,7 @@ "plugin": [ "lib/plugin" ], - "client": [ + "beta/client": [ "lib/client/index" ] } diff --git a/js/genkit/src/client/client.ts b/js/genkit/src/client/client.ts index 6caeb9096..05e5d3256 100644 --- a/js/genkit/src/client/client.ts +++ b/js/genkit/src/client/client.ts @@ -24,7 +24,7 @@ const __flowStreamDelimiter = '\n\n'; * For example: * * ```js - * import { streamFlow } from 'genkit/client'; + * import { streamFlow } from 'genkit/beta/client'; * * const response = streamFlow({ * url: 'https://my-flow-deployed-url', diff --git a/js/plugins/express/README.md b/js/plugins/express/README.md index f50bacd4e..b7b610f6a 100644 --- a/js/plugins/express/README.md +++ b/js/plugins/express/README.md @@ -52,10 +52,10 @@ app.post( ); ``` -Flows and actions exposed using the `expressHandler` function can be accessed using `genkit/client` library: +Flows and actions exposed using the `expressHandler` function can be accessed using `genkit/beta/client` library: ```ts -import { runFlow, streamFlow } from 'genkit/client'; +import { runFlow, streamFlow } from 'genkit/beta/client'; const result = await runFlow({ url: `http://localhost:${port}/simpleFlow`, diff --git a/js/plugins/express/tests/express_test.ts b/js/plugins/express/tests/express_test.ts index 0867809b4..7c8634953 100644 --- a/js/plugins/express/tests/express_test.ts +++ b/js/plugins/express/tests/express_test.ts @@ -17,7 +17,7 @@ import * as assert from 'assert'; import express from 'express'; import { GenerateResponseData, Genkit, genkit, z } from 'genkit'; -import { runFlow, streamFlow } from 'genkit/client'; +import { runFlow, streamFlow } from 'genkit/beta/client'; import { GenerateResponseChunkData, ModelAction } from 'genkit/model'; import getPort from 'get-port'; import * as http from 'http'; diff --git a/js/plugins/firebase/tests/functions_test.ts b/js/plugins/firebase/tests/functions_test.ts index e31f65798..14e050bdc 100644 --- a/js/plugins/firebase/tests/functions_test.ts +++ b/js/plugins/firebase/tests/functions_test.ts @@ -18,7 +18,7 @@ import * as express from 'express'; import { initializeApp } from 'firebase/app'; import { getFunctions, httpsCallableFromURL } from 'firebase/functions'; import { Genkit, genkit } from 'genkit'; -import { runFlow, streamFlow } from 'genkit/client'; +import { runFlow, streamFlow } from 'genkit/beta/client'; import * as getPort from 'get-port'; import * as http from 'http'; import { RequestWithAuth, noAuth, onFlow } from '../lib/functions.js'; diff --git a/js/plugins/next/README.md b/js/plugins/next/README.md index 93cb880aa..fede4b50d 100644 --- a/js/plugins/next/README.md +++ b/js/plugins/next/README.md @@ -25,7 +25,7 @@ import { appRoute } from '@genkit-ai/nextjs'; export const POST = appRoute(simpleFlow); ``` -APIs can be called with the generic `genkit/client` library, or `@genkit-ai/nextjs/client` +APIs can be called with the generic `genkit/beta/client` library, or `@genkit-ai/nextjs/client` ```ts import { runFlow, streamFlow } from '@genkit-ai/nextjs/client'; diff --git a/js/plugins/next/src/client.ts b/js/plugins/next/src/client.ts index be41ae6ec..6692d0031 100644 --- a/js/plugins/next/src/client.ts +++ b/js/plugins/next/src/client.ts @@ -18,7 +18,7 @@ import { Action } from '@genkit-ai/core'; import { runFlow as baseRunFlow, streamFlow as baseStreamFlow, -} from 'genkit/client'; +} from 'genkit/beta/client'; type Input = A extends Action ? I['_output'] : never; diff --git a/tests/src/flow_server_test.ts b/tests/src/flow_server_test.ts index 5b301035b..e1e47dcbd 100644 --- a/tests/src/flow_server_test.ts +++ b/tests/src/flow_server_test.ts @@ -15,7 +15,7 @@ */ import { readFileSync } from 'fs'; -import { streamFlow } from 'genkit/client'; +import { streamFlow } from 'genkit/beta/client'; import * as yaml from 'yaml'; import { retriable, runTestsForApp } from './utils.js'; From 9d419a569b920eec4f1f611e9ebeae6151119db6 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Mon, 3 Feb 2025 17:07:25 -0500 Subject: [PATCH 501/562] chore(js/plugins/vertexai): bump vertexai SDK to 1.9.3 (#1805) --- js/plugins/vertexai/package.json | 2 +- js/pnpm-lock.yaml | 78 +++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 5e81a1cf5..e1c7b3ca7 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -39,7 +39,7 @@ "@anthropic-ai/vertex-sdk": "^0.4.0", "@google-cloud/aiplatform": "^3.23.0", "@mistralai/mistralai-gcp": "^1.3.5", - "@google-cloud/vertexai": "^1.9.2", + "@google-cloud/vertexai": "^1.9.3", "google-auth-library": "^9.14.2", "googleapis": "^140.0.1", "node-fetch": "^3.3.2", diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 2ca35d8c5..412999447 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -784,8 +784,8 @@ importers: specifier: ^3.23.0 version: 3.25.0(encoding@0.1.13) '@google-cloud/vertexai': - specifier: ^1.9.2 - version: 1.9.2(encoding@0.1.13) + specifier: ^1.9.3 + version: 1.9.3(encoding@0.1.13) '@mistralai/mistralai-gcp': specifier: ^1.3.5 version: 1.3.5(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.24.1) @@ -1373,7 +1373,7 @@ importers: version: link:../../plugins/ollama genkitx-openai: specifier: ^0.10.1 - version: 0.10.1(@genkit-ai/ai@1.0.0-rc.12)(@genkit-ai/core@1.0.0-rc.12) + version: 0.10.1(@genkit-ai/ai@1.0.0-rc.14)(@genkit-ai/core@1.0.0-rc.14) devDependencies: rimraf: specifier: ^6.0.1 @@ -1496,6 +1496,52 @@ importers: specifier: ^5.6.2 version: 5.6.3 + testapps/personal-testing: + dependencies: + '@genkit-ai/firebase': + specifier: workspace:* + version: link:../../plugins/firebase + '@genkit-ai/google-cloud': + specifier: workspace:* + version: link:../../plugins/google-cloud + '@genkit-ai/googleai': + specifier: workspace:* + version: link:../../plugins/googleai + '@genkit-ai/vertexai': + specifier: workspace:* + version: link:../../plugins/vertexai + '@google/generative-ai': + specifier: ^0.15.0 + version: 0.15.0 + '@opentelemetry/sdk-trace-base': + specifier: ^1.25.0 + version: 1.26.0(@opentelemetry/api@1.9.0) + body-parser: + specifier: ^1.20.3 + version: 1.20.3 + express: + specifier: ^4.21.0 + version: 4.21.1 + firebase-admin: + specifier: '>=12.2' + version: 12.3.1(encoding@0.1.13) + genkit: + specifier: workspace:* + version: link:../../genkit + partial-json: + specifier: ^0.1.7 + version: 0.1.7 + devDependencies: + rimraf: + specifier: ^6.0.1 + version: 6.0.1 + tsx: + specifier: ^4.19.2 + version: 4.19.2 + typescript: + specifier: ^5.3.3 + version: 5.6.3 + testapps/prompt-file: dependencies: '@genkit-ai/googleai': @@ -2505,11 +2551,11 @@ packages: '@firebase/webchannel-wrapper@1.0.3': resolution: {integrity: sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==} - '@genkit-ai/ai@1.0.0-rc.12': - resolution: {integrity: sha512-1hofDfuTEVDfryy7klgeJwCyXOp4wEZ+VPU34Dts9S5PYoN1nb23KAhnz+6dHgM42REM1PI5Zh2Wq8O10BXIww==} + '@genkit-ai/ai@1.0.0-rc.14': + resolution: {integrity: sha512-5CHyRixumiM5uHkzwHl7IWbrYSzWvhDvYAsMoq9mmI8iCJA9g/3u8JOcHSRiseuaap5gv/QGm809IpIlak55ZA==} - '@genkit-ai/core@1.0.0-rc.12': - resolution: {integrity: sha512-3K7GVXR1vnJu2ANICbojxKpBoxIncuGIvn0NJ7T+s3/IOBQcusmkUyJoc4TdeRsZdX693J+oMUSx+ZE18s28eQ==} + '@genkit-ai/core@1.0.0-rc.14': + resolution: {integrity: sha512-8h0Vl3SCP0vYIN60uELaZWEAP6BjLxXTcoWv2MFn7khxblzVkMWq98IxPThHo3s5BGB+Pxnhnsywdb/ItliFUA==} '@gerrit0/mini-shiki@1.24.4': resolution: {integrity: sha512-YEHW1QeAg6UmxEmswiQbOVEg1CW22b1XUD/lNTliOsu0LD0wqoyleFMnmbTp697QE0pcadQiR5cVtbbAPncvpw==} @@ -2592,8 +2638,8 @@ packages: resolution: {integrity: sha512-sZW14pfxEQZSIbBPs6doFYtcbK31Bs3E4jH5Ly3jJnBkYfkMPX8sXG3ZQXCJa88MKtUNPlgBdMN2OJUzmFe5/g==} engines: {node: '>=14'} - '@google-cloud/vertexai@1.9.2': - resolution: {integrity: sha512-pJSUG3r5QIvCFNfkz7/y7kEqvEJaVAk0jZbZoKbcPCRUnXaUeAq7p8I0oklqetGyxbUcZ2FOGpt+Y+4uIltVPg==} + '@google-cloud/vertexai@1.9.3': + resolution: {integrity: sha512-35o5tIEMLW3JeFJOaaMNR2e5sq+6rpnhrF97PuAxeOm0GlqVTESKhkGj7a5B5mmJSSSU3hUfIhcQCRRsw4Ipzg==} engines: {node: '>=18.0.0'} '@google/generative-ai@0.15.0': @@ -7822,9 +7868,9 @@ snapshots: '@firebase/webchannel-wrapper@1.0.3': {} - '@genkit-ai/ai@1.0.0-rc.12': + '@genkit-ai/ai@1.0.0-rc.14': dependencies: - '@genkit-ai/core': 1.0.0-rc.12 + '@genkit-ai/core': 1.0.0-rc.14 '@opentelemetry/api': 1.9.0 '@types/node': 20.16.9 colorette: 2.0.20 @@ -7836,7 +7882,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@genkit-ai/core@1.0.0-rc.12': + '@genkit-ai/core@1.0.0-rc.14': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) @@ -8033,7 +8079,7 @@ snapshots: - supports-color optional: true - '@google-cloud/vertexai@1.9.2(encoding@0.1.13)': + '@google-cloud/vertexai@1.9.3(encoding@0.1.13)': dependencies: google-auth-library: 9.14.2(encoding@0.1.13) transitivePeerDependencies: @@ -10578,10 +10624,10 @@ snapshots: - encoding - supports-color - genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-rc.12)(@genkit-ai/core@1.0.0-rc.12): + genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-rc.14)(@genkit-ai/core@1.0.0-rc.14): dependencies: - '@genkit-ai/ai': 1.0.0-rc.12 - '@genkit-ai/core': 1.0.0-rc.12 + '@genkit-ai/ai': 1.0.0-rc.14 + '@genkit-ai/core': 1.0.0-rc.14 openai: 4.53.0(encoding@0.1.13) zod: 3.24.1 transitivePeerDependencies: From 4b21318615d0831365ed7c0d9bb1d4c51fd29dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20St=C3=A6rk=C3=A6r?= Date: Mon, 3 Feb 2025 23:30:15 +0100 Subject: [PATCH 502/562] fix: uses the provided sessionId from session options if provided (#1737) --- js/genkit/src/beta.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/src/beta.ts b/js/genkit/src/beta.ts index 96af62885..01da2b6db 100644 --- a/js/genkit/src/beta.ts +++ b/js/genkit/src/beta.ts @@ -131,7 +131,7 @@ export class GenkitBeta extends Genkit { * Create a session for this environment. */ createSession(options?: SessionOptions): Session { - const sessionId = uuidv4(); + const sessionId = options?.sessionId?.trim() || uuidv4(); const sessionData: SessionData = { id: sessionId, state: options?.initialState, From 56ce1c65b3d14e7a46e3658be1ee2bf4184d64e6 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Mon, 3 Feb 2025 15:58:03 -0800 Subject: [PATCH 503/562] fix(js): Make flow.run's first parameter optional (#1810) --- js/core/src/action.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/src/action.ts b/js/core/src/action.ts index 7683d9893..4fee1628c 100644 --- a/js/core/src/action.ts +++ b/js/core/src/action.ts @@ -119,7 +119,7 @@ export type Action< __action: ActionMetadata; __registry: Registry; run( - input: z.infer, + input?: z.infer, options?: ActionRunOptions> ): Promise>>; From 875b55e53b9d51b8b228a51347f7318826d84fac Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Mon, 3 Feb 2025 15:58:20 -0800 Subject: [PATCH 504/562] chore(js): Rename streamingCallback to sendChunk (#1809) --- docs/flows.md | 9 ++-- js/doc-snippets/src/flows/index.ts | 14 +++-- js/genkit/src/client/client.ts | 8 +-- js/genkit/tests/helpers.ts | 18 +++---- js/plugins/express/README.md | 19 +++---- js/plugins/googleai/src/gemini.ts | 6 +-- .../vertexai/src/modelgarden/anthropic.ts | 6 +-- .../vertexai/src/modelgarden/mistral.ts | 6 +-- .../src/modelgarden/openai_compatibility.ts | 6 +-- js/pnpm-lock.yaml | 46 ---------------- js/testapps/dev-ui-gallery/src/main/flows.ts | 24 ++++----- js/testapps/express/src/index.ts | 4 +- js/testapps/flow-sample1/src/index.ts | 24 ++++----- js/testapps/flow-simple-ai/src/index.ts | 52 +++++++------------ js/testapps/multimodal/src/pdf.ts | 4 +- js/testapps/multimodal/src/video.ts | 4 +- js/testapps/prompt-file/src/index.ts | 21 +++----- js/testapps/rag/src/pdf-rag.ts | 4 +- .../js-angular/server/src/jsonStreaming.ts | 10 ++-- samples/js-chatbot/server/src/index.ts | 4 +- tests/test_js_app/src/index.ts | 10 ++-- 21 files changed, 107 insertions(+), 192 deletions(-) diff --git a/docs/flows.md b/docs/flows.md index 6bc982eea..2a88b22c2 100644 --- a/docs/flows.md +++ b/docs/flows.md @@ -109,12 +109,11 @@ Here's an example of a flow that supports streaming: * The `streamSchema` option specifies the type of values your flow streams. This does not necessarily need to be the same type as the `outputSchema`, which is the type of the flow's complete output. -* `streamingCallback` is a callback function that takes a single parameter, of +* The second parameter to your flow definition is called "sideChannel". It provides + multiple useful features, such as request context and the `sendChunk` callback. + The `sendChunk` callback takes a single parameter, of the type specified by `streamSchema`. Whenever data becomes available within - your flow, send the data to the output stream by calling this function. Note - that `streamingCallback` is only defined if the caller of your flow - requested streaming output, so you need to check that it's defined before - calling it. + your flow, send the data to the output stream by calling this function. In the above example, the values streamed by the flow are directly coupled to the values streamed by the `generate()` call inside the flow. Although this is diff --git a/js/doc-snippets/src/flows/index.ts b/js/doc-snippets/src/flows/index.ts index 938a82528..219f6ce37 100644 --- a/js/doc-snippets/src/flows/index.ts +++ b/js/doc-snippets/src/flows/index.ts @@ -92,19 +92,17 @@ export const menuSuggestionStreamingFlow = ai.defineFlow( streamSchema: z.string(), outputSchema: z.object({ theme: z.string(), menuItem: z.string() }), }, - async (restaurantTheme, streamingCallback) => { + async (restaurantTheme, { sendChunk }) => { const response = await ai.generateStream({ model: gemini15Flash, prompt: `Invent a menu item for a ${restaurantTheme} themed restaurant.`, }); - if (streamingCallback) { - for await (const chunk of response.stream) { - // Here, you could process the chunk in some way before sending it to - // the output stream via streamingCallback(). In this example, we output - // the text of the chunk, unmodified. - streamingCallback(chunk.text); - } + for await (const chunk of response.stream) { + // Here, you could process the chunk in some way before sending it to + // the output stream via streamingCallback(). In this example, we output + // the text of the chunk, unmodified. + sendChunk(chunk.text); } return { diff --git a/js/genkit/src/client/client.ts b/js/genkit/src/client/client.ts index 05e5d3256..dd42cabb8 100644 --- a/js/genkit/src/client/client.ts +++ b/js/genkit/src/client/client.ts @@ -53,7 +53,7 @@ export function streamFlow({ const operationPromise = __flowRunEnvelope({ url, input, - streamingCallback: (c) => channel.send(c), + sendChunk: (c) => channel.send(c), headers, }); operationPromise.then( @@ -70,12 +70,12 @@ export function streamFlow({ async function __flowRunEnvelope({ url, input, - streamingCallback, + sendChunk, headers, }: { url: string; input: any; - streamingCallback: (chunk: any) => void; + sendChunk: (chunk: any) => void; headers?: Record; }) { const response = await fetch(url, { @@ -115,7 +115,7 @@ async function __flowRunEnvelope({ .substring('data: '.length) ); if (chunk.hasOwnProperty('message')) { - streamingCallback(chunk.message); + sendChunk(chunk.message); } else if (chunk.hasOwnProperty('result')) { return chunk.result; } else if (chunk.hasOwnProperty('error')) { diff --git a/js/genkit/tests/helpers.ts b/js/genkit/tests/helpers.ts index e5dcddcd1..32df0ae39 100644 --- a/js/genkit/tests/helpers.ts +++ b/js/genkit/tests/helpers.ts @@ -31,12 +31,12 @@ export function defineEchoModel(ai: Genkit): ModelAction { { name: 'echoModel', }, - async (request, streamingCallback) => { + async (request, sendChunk) => { (model as any).__test__lastRequest = request; - (model as any).__test__lastStreamingCallback = streamingCallback; - if (streamingCallback) { + (model as any).__test__lastStreamingCallback = sendChunk; + if (sendChunk) { await runAsync(() => { - streamingCallback({ + sendChunk({ content: [ { text: '3', @@ -45,7 +45,7 @@ export function defineEchoModel(ai: Genkit): ModelAction { }); }); await runAsync(() => { - streamingCallback({ + sendChunk({ content: [ { text: '2', @@ -54,7 +54,7 @@ export function defineEchoModel(ai: Genkit): ModelAction { }); }); await runAsync(() => { - streamingCallback({ + sendChunk({ content: [ { text: '1', @@ -148,7 +148,7 @@ export function defineStaticResponseModel( export type ProgrammableModel = ModelAction & { handleResponse: ( req: GenerateRequest, - streamingCallback?: StreamingCallback + sendChunk?: StreamingCallback ) => Promise; lastRequest?: GenerateRequest; @@ -162,9 +162,9 @@ export function defineProgrammableModel(ai: Genkit): ProgrammableModel { tools: true, }, }, - async (request, streamingCallback) => { + async (request, sendChunk) => { pm.lastRequest = JSON.parse(JSON.stringify(request)); - return pm.handleResponse(request, streamingCallback); + return pm.handleResponse(request, sendChunk); } ) as ProgrammableModel; diff --git a/js/plugins/express/README.md b/js/plugins/express/README.md index b7b610f6a..95c1a2ada 100644 --- a/js/plugins/express/README.md +++ b/js/plugins/express/README.md @@ -6,17 +6,14 @@ This plugin provides utilities for conveninetly exposing Genkit flows and action import { expressHandler } from '@genkit-ai/express'; import express from 'express'; -const simpleFlow = ai.defineFlow( - 'simpleFlow', - async (input, streamingCallback) => { - const { text } = await ai.generate({ - model: gemini15Flash, - prompt: input, - streamingCallback, - }); - return text; - } -); +const simpleFlow = ai.defineFlow('simpleFlow', async (input, { sendChunk }) => { + const { text } = await ai.generate({ + model: gemini15Flash, + prompt: input, + onChunk: (c) => sendChunk(c.text), + }); + return text; +}); const app = express(); app.use(express.json()); diff --git a/js/plugins/googleai/src/gemini.ts b/js/plugins/googleai/src/gemini.ts index cace611fe..92a2e0632 100644 --- a/js/plugins/googleai/src/gemini.ts +++ b/js/plugins/googleai/src/gemini.ts @@ -646,7 +646,7 @@ export function defineGoogleAIModel( configSchema: model.configSchema, use: middleware, }, - async (request, streamingCallback) => { + async (request, sendChunk) => { const options: RequestOptions = { apiClient: GENKIT_CLIENT_HEADER }; if (apiVersion) { options.apiVersion = apiVersion; @@ -780,14 +780,14 @@ export function defineGoogleAIModel( ); } - if (streamingCallback) { + if (sendChunk) { const result = await genModel .startChat(updatedChatRequest) .sendMessageStream(msg.parts, options); for await (const item of result.stream) { (item as GenerateContentResponse).candidates?.forEach((candidate) => { const c = fromJSONModeScopedGeminiCandidate(candidate); - streamingCallback({ + sendChunk({ index: c.index, content: c.message.content, }); diff --git a/js/plugins/vertexai/src/modelgarden/anthropic.ts b/js/plugins/vertexai/src/modelgarden/anthropic.ts index 850f393a5..9fac5c5fd 100644 --- a/js/plugins/vertexai/src/modelgarden/anthropic.ts +++ b/js/plugins/vertexai/src/modelgarden/anthropic.ts @@ -409,9 +409,9 @@ export function anthropicModel( supports: model.info?.supports, versions: model.info?.versions, }, - async (input, streamingCallback) => { + async (input, sendChunk) => { const client = clientFactory(input.config?.location || region); - if (!streamingCallback) { + if (!sendChunk) { const response = await client.messages.create({ ...toAnthropicRequest(input.config?.version ?? modelName, input), stream: false, @@ -423,7 +423,7 @@ export function anthropicModel( ); for await (const event of stream) { if (event.type === 'content_block_delta') { - streamingCallback({ + sendChunk({ index: 0, content: [ { diff --git a/js/plugins/vertexai/src/modelgarden/mistral.ts b/js/plugins/vertexai/src/modelgarden/mistral.ts index b2aac4dde..88ec9d24f 100644 --- a/js/plugins/vertexai/src/modelgarden/mistral.ts +++ b/js/plugins/vertexai/src/modelgarden/mistral.ts @@ -341,13 +341,13 @@ export function mistralModel( supports: model.info?.supports, versions: model.info?.versions, }, - async (input, streamingCallback) => { + async (input, sendChunk) => { const client = getClient(input.config?.location || region); const versionedModel = input.config?.version ?? model.info?.versions?.[0] ?? model.name; - if (!streamingCallback) { + if (!sendChunk) { const mistralRequest = toMistralRequest(versionedModel, input); const response = await client.chat.complete(mistralRequest, { @@ -372,7 +372,7 @@ export function mistralModel( for await (const event of stream) { const parts = fromMistralCompletionChunk(event.data); if (parts.length > 0) { - streamingCallback({ + sendChunk({ content: parts, }); } diff --git a/js/plugins/vertexai/src/modelgarden/openai_compatibility.ts b/js/plugins/vertexai/src/modelgarden/openai_compatibility.ts index 2de914f57..36b023a37 100644 --- a/js/plugins/vertexai/src/modelgarden/openai_compatibility.ts +++ b/js/plugins/vertexai/src/modelgarden/openai_compatibility.ts @@ -311,12 +311,12 @@ export function openaiCompatibleModel( }, async ( request: GenerateRequest, - streamingCallback?: StreamingCallback + sendChunk?: StreamingCallback ): Promise => { let response: ChatCompletion; const client = await clientFactory(request); const body = toRequestBody(model, request); - if (streamingCallback) { + if (sendChunk) { const stream = client.beta.chat.completions.stream({ ...body, stream: true, @@ -324,7 +324,7 @@ export function openaiCompatibleModel( for await (const chunk of stream) { chunk.choices?.forEach((chunk) => { const c = fromOpenAiChunkChoice(chunk); - streamingCallback({ + sendChunk({ index: c.index, content: c.message.content, }); diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 412999447..a2d56992b 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -1496,52 +1496,6 @@ importers: specifier: ^5.6.2 version: 5.6.3 - testapps/personal-testing: - dependencies: - '@genkit-ai/firebase': - specifier: workspace:* - version: link:../../plugins/firebase - '@genkit-ai/google-cloud': - specifier: workspace:* - version: link:../../plugins/google-cloud - '@genkit-ai/googleai': - specifier: workspace:* - version: link:../../plugins/googleai - '@genkit-ai/vertexai': - specifier: workspace:* - version: link:../../plugins/vertexai - '@google/generative-ai': - specifier: ^0.15.0 - version: 0.15.0 - '@opentelemetry/sdk-trace-base': - specifier: ^1.25.0 - version: 1.26.0(@opentelemetry/api@1.9.0) - body-parser: - specifier: ^1.20.3 - version: 1.20.3 - express: - specifier: ^4.21.0 - version: 4.21.1 - firebase-admin: - specifier: '>=12.2' - version: 12.3.1(encoding@0.1.13) - genkit: - specifier: workspace:* - version: link:../../genkit - partial-json: - specifier: ^0.1.7 - version: 0.1.7 - devDependencies: - rimraf: - specifier: ^6.0.1 - version: 6.0.1 - tsx: - specifier: ^4.19.2 - version: 4.19.2 - typescript: - specifier: ^5.3.3 - version: 5.6.3 - testapps/prompt-file: dependencies: '@genkit-ai/googleai': diff --git a/js/testapps/dev-ui-gallery/src/main/flows.ts b/js/testapps/dev-ui-gallery/src/main/flows.ts index fa9bc346c..01ca8239a 100644 --- a/js/testapps/dev-ui-gallery/src/main/flows.ts +++ b/js/testapps/dev-ui-gallery/src/main/flows.ts @@ -83,13 +83,11 @@ ai.defineFlow( outputSchema: z.string(), streamSchema: z.number(), }, - async (count, streamingCallback) => { + async (count, { sendChunk }) => { let i = 1; - if (streamingCallback) { - for (; i <= count; i++) { - await new Promise((r) => setTimeout(r, 500)); - streamingCallback(i); - } + for (; i <= count; i++) { + await new Promise((r) => setTimeout(r, 500)); + sendChunk(i); } return `done: ${count}, streamed: ${i - 1} times`; } @@ -168,16 +166,14 @@ ai.defineFlow( outputSchema: z.string(), streamSchema: z.number(), }, - async (count, streamingCallback) => { + async (count, { sendChunk }) => { let i = 1; - if (streamingCallback) { - for (; i <= count; i++) { - if (i == 3) { - throw new Error('I cannot count that high!'); - } - await new Promise((r) => setTimeout(r, 500)); - streamingCallback(i); + for (; i <= count; i++) { + if (i == 3) { + throw new Error('I cannot count that high!'); } + await new Promise((r) => setTimeout(r, 500)); + sendChunk(i); } if (count) { throw new Error('I cannot count that low!'); diff --git a/js/testapps/express/src/index.ts b/js/testapps/express/src/index.ts index 8e6cc0019..4c4ae6ed1 100644 --- a/js/testapps/express/src/index.ts +++ b/js/testapps/express/src/index.ts @@ -49,7 +49,7 @@ const ai = genkit({ export const jokeFlow = ai.defineFlow( { name: 'jokeFlow', inputSchema: z.string(), outputSchema: z.string() }, - async (subject, streamingCallback) => { + async (subject, { sendChunk }) => { return await ai.run('call-llm', async () => { const llmResponse = await ai.generate({ prompt: `tell me long joke about ${subject}`, @@ -57,7 +57,7 @@ export const jokeFlow = ai.defineFlow( config: { temperature: 1, }, - streamingCallback, + onChunk: (c) => sendChunk(c.text), }); return llmResponse.text; diff --git a/js/testapps/flow-sample1/src/index.ts b/js/testapps/flow-sample1/src/index.ts index d75927e32..d2ac1a21e 100644 --- a/js/testapps/flow-sample1/src/index.ts +++ b/js/testapps/flow-sample1/src/index.ts @@ -70,13 +70,11 @@ export const streamy = ai.defineFlow( outputSchema: z.string(), streamSchema: z.object({ count: z.number() }), }, - async (count, streamingCallback) => { + async (count, { sendChunk }) => { let i = 0; - if (streamingCallback) { - for (; i < count; i++) { - await new Promise((r) => setTimeout(r, 1000)); - streamingCallback({ count: i }); - } + for (; i < count; i++) { + await new Promise((r) => setTimeout(r, 1000)); + sendChunk({ count: i }); } return `done: ${count}, streamed: ${i} times`; } @@ -90,16 +88,14 @@ export const streamyThrowy = ai.defineFlow( outputSchema: z.string(), streamSchema: z.object({ count: z.number() }), }, - async (count, streamingCallback) => { + async (count, { sendChunk }) => { let i = 0; - if (streamingCallback) { - for (; i < count; i++) { - if (i == 3) { - throw new Error('whoops'); - } - await new Promise((r) => setTimeout(r, 1000)); - streamingCallback({ count: i }); + for (; i < count; i++) { + if (i == 3) { + throw new Error('whoops'); } + await new Promise((r) => setTimeout(r, 1000)); + sendChunk({ count: i }); } return `done: ${count}, streamed: ${i} times`; } diff --git a/js/testapps/flow-simple-ai/src/index.ts b/js/testapps/flow-simple-ai/src/index.ts index a73afda44..79da26e11 100644 --- a/js/testapps/flow-simple-ai/src/index.ts +++ b/js/testapps/flow-simple-ai/src/index.ts @@ -129,16 +129,14 @@ export const streamFlow = ai.defineFlow( outputSchema: z.string(), streamSchema: z.string(), }, - async (prompt, streamingCallback) => { - const { response, stream } = await ai.generateStream({ + async (prompt, { sendChunk }) => { + const { response, stream } = ai.generateStream({ model: gemini15Flash, prompt, }); - if (streamingCallback) { - for await (const chunk of stream) { - streamingCallback(chunk.content[0].text!); - } + for await (const chunk of stream) { + sendChunk(chunk.content[0].text!); } return (await response).text; @@ -167,12 +165,8 @@ export const streamJsonFlow = ai.defineFlow( outputSchema: z.string(), streamSchema: GameCharactersSchema, }, - async (count, streamingCallback) => { - if (!streamingCallback) { - throw new Error('this flow only works in streaming mode'); - } - - const { response, stream } = await ai.generateStream({ + async (count, { sendChunk }) => { + const { response, stream } = ai.generateStream({ model: gemini15Flash, output: { schema: GameCharactersSchema, @@ -184,7 +178,7 @@ export const streamJsonFlow = ai.defineFlow( for await (const chunk of stream) { buffer += chunk.content[0].text!; if (buffer.length > 10) { - streamingCallback(parse(maybeStripMarkdown(buffer), Allow.ALL)); + sendChunk(parse(maybeStripMarkdown(buffer), Allow.ALL)); } } @@ -270,12 +264,12 @@ export const vertexStreamer = ai.defineFlow( inputSchema: z.string(), outputSchema: z.string(), }, - async (input, streamingCallback) => { + async (input, { sendChunk }) => { return await ai.run('call-llm', async () => { const llmResponse = await ai.generate({ model: gemini15Flash, prompt: `Tell me a very long joke about ${input}.`, - streamingCallback, + onChunk: (c) => sendChunk(c.text), }); return llmResponse.text; @@ -407,12 +401,8 @@ export const toolCaller = ai.defineFlow( outputSchema: z.string(), streamSchema: z.any(), }, - async (_, streamingCallback) => { - if (!streamingCallback) { - throw new Error('this flow only works in streaming mode'); - } - - const { response, stream } = await ai.generateStream({ + async (_, { sendChunk }) => { + const { response, stream } = ai.generateStream({ model: gemini15Flash, config: { temperature: 1, @@ -422,7 +412,7 @@ export const toolCaller = ai.defineFlow( }); for await (const chunk of stream) { - streamingCallback(chunk); + sendChunk(chunk); } return (await response).text; @@ -449,12 +439,8 @@ export const forcedToolCaller = ai.defineFlow( outputSchema: z.string(), streamSchema: z.any(), }, - async (input, streamingCallback) => { - if (!streamingCallback) { - throw new Error('this flow only works in streaming mode'); - } - - const { response, stream } = await ai.generateStream({ + async (input, { sendChunk }) => { + const { response, stream } = ai.generateStream({ model: gemini15Flash, config: { temperature: 1, @@ -465,7 +451,7 @@ export const forcedToolCaller = ai.defineFlow( }); for await (const chunk of stream) { - streamingCallback(chunk); + sendChunk(chunk); } return (await response).text; @@ -573,9 +559,9 @@ export const arrayStreamTester = ai.defineFlow( outputSchema: z.any(), streamSchema: z.any(), }, - async (input, streamingCallback) => { + async (input, { sendChunk }) => { try { - const { stream, response } = await ai.generateStream({ + const { stream, response } = ai.generateStream({ model: gemini15Flash, config: { safetySettings: [ @@ -612,7 +598,7 @@ export const arrayStreamTester = ai.defineFlow( }); for await (const { output, text } of stream) { - streamingCallback?.({ text, output }); + sendChunk({ text, output }); } const result = await response; @@ -645,7 +631,7 @@ ai.defineModel( { name: 'hiModel', }, - async (request, streamingCallback) => { + async () => { return { finishReason: 'stop', message: { role: 'model', content: [{ text: 'hi' }] }, diff --git a/js/testapps/multimodal/src/pdf.ts b/js/testapps/multimodal/src/pdf.ts index 2b4e878b9..c53ea4913 100644 --- a/js/testapps/multimodal/src/pdf.ts +++ b/js/testapps/multimodal/src/pdf.ts @@ -43,7 +43,7 @@ export const multimodalPdfQAFlow = ai.defineFlow( inputSchema: z.string(), outputSchema: z.string(), }, - async (query: any, streamingCallback: any) => { + async (query: string, { sendChunk }) => { const docs = (await ai.retrieve({ retriever: pdfMultimodalRetriever, query, @@ -74,7 +74,7 @@ export const multimodalPdfQAFlow = ai.defineFlow( }), }, { - streamingCallback, + onChunk: (c) => sendChunk(c.text), } ).then((r) => r.text); } diff --git a/js/testapps/multimodal/src/video.ts b/js/testapps/multimodal/src/video.ts index fe50d42cb..9902e9c8e 100644 --- a/js/testapps/multimodal/src/video.ts +++ b/js/testapps/multimodal/src/video.ts @@ -138,7 +138,7 @@ export const VideoQAFlow = ai.defineFlow( inputSchema: z.string(), outputSchema: z.string(), }, - async (query: any, streamingCallback: any) => { + async (query, { sendChunk }) => { const docs = (await ai.retrieve({ retriever: videoRetriever, query, @@ -166,7 +166,7 @@ export const VideoQAFlow = ai.defineFlow( })[0], }, { - streamingCallback, + onChunk: (c) => sendChunk(c.text), } ).then((r) => r.text); } diff --git a/js/testapps/prompt-file/src/index.ts b/js/testapps/prompt-file/src/index.ts index f1596d8cc..6a1060d07 100644 --- a/js/testapps/prompt-file/src/index.ts +++ b/js/testapps/prompt-file/src/index.ts @@ -87,20 +87,15 @@ ai.defineFlow( outputSchema: z.string(), streamSchema: z.string(), }, - async ({ subject, personality }, streamingCallback) => { + async ({ subject, personality }, { sendChunk }) => { const storyPrompt = ai.prompt('story'); - if (streamingCallback) { - const { response, stream } = await storyPrompt.stream({ - subject, - personality, - }); - for await (const chunk of stream) { - streamingCallback(chunk.content[0]?.text!); - } - return (await response).text; - } else { - const response = await storyPrompt({ subject }); - return response.text; + const { response, stream } = storyPrompt.stream({ + subject, + personality, + }); + for await (const chunk of stream) { + sendChunk(chunk.content[0]?.text!); } + return (await response).text; } ); diff --git a/js/testapps/rag/src/pdf-rag.ts b/js/testapps/rag/src/pdf-rag.ts index e3b33ca5f..63a9fcf0f 100644 --- a/js/testapps/rag/src/pdf-rag.ts +++ b/js/testapps/rag/src/pdf-rag.ts @@ -38,7 +38,7 @@ export const pdfQA = ai.defineFlow( inputSchema: z.string(), outputSchema: z.string(), }, - async (query, streamingCallback) => { + async (query, { sendChunk }) => { const docs = await ai.retrieve({ retriever: pdfChatRetriever, query, @@ -51,7 +51,7 @@ export const pdfQA = ai.defineFlow( context: docs.map((d) => d.text), }, { - streamingCallback, + onChunk: (c) => sendChunk(c.text), } ).then((r) => r.text); } diff --git a/samples/js-angular/server/src/jsonStreaming.ts b/samples/js-angular/server/src/jsonStreaming.ts index df0611100..ef07b7c62 100644 --- a/samples/js-angular/server/src/jsonStreaming.ts +++ b/samples/js-angular/server/src/jsonStreaming.ts @@ -34,18 +34,14 @@ const GameCharactersSchema = z.object({ .describe('Characters'), }); -export const streamCharacters = ai.defineStreamingFlow( +export const streamCharacters = ai.defineFlow( { name: 'streamCharacters', inputSchema: z.number(), outputSchema: z.string(), streamSchema: GameCharactersSchema, }, - async (count, streamingCallback) => { - if (!streamingCallback) { - throw new Error('this flow only works in streaming mode'); - } - + async (count, { sendChunk }) => { const { response, stream } = await ai.generateStream({ model: gemini15Flash, output: { @@ -62,7 +58,7 @@ export const streamCharacters = ai.defineStreamingFlow( for await (const chunk of stream) { buffer += chunk.content[0].text!; if (buffer.length > 10) { - streamingCallback(parse(maybeStripMarkdown(buffer), Allow.ALL)); + sendChunk(parse(maybeStripMarkdown(buffer), Allow.ALL)); } } diff --git a/samples/js-chatbot/server/src/index.ts b/samples/js-chatbot/server/src/index.ts index 283b368a2..ddd4c4454 100644 --- a/samples/js-chatbot/server/src/index.ts +++ b/samples/js-chatbot/server/src/index.ts @@ -63,7 +63,7 @@ export const chatbotFlow = ai.defineFlow( outputSchema: z.string(), streamSchema: GenerateResponseChunkSchema, }, - async (request, streamingCallback) => { + async (request, { sendChunk }) => { // Retrieve conversation history. const history = await ai.run( 'retrieve-history', @@ -78,7 +78,7 @@ export const chatbotFlow = ai.defineFlow( prompt: request.prompt, messages: history, model: llms[request.llmIndex], - streamingCallback, + onChunk: (c) => sendChunk(c.text), }); // Save history. diff --git a/tests/test_js_app/src/index.ts b/tests/test_js_app/src/index.ts index 72b288645..2919e4fdc 100644 --- a/tests/test_js_app/src/index.ts +++ b/tests/test_js_app/src/index.ts @@ -69,13 +69,11 @@ export const streamy = ai.defineFlow( outputSchema: z.string(), streamSchema: z.object({ count: z.number() }), }, - async (count, streamingCallback) => { + async (count, { sendChunk }) => { let i = 0; - if (streamingCallback) { - for (; i < count; i++) { - await new Promise((r) => setTimeout(r, 1000)); - streamingCallback({ count: i }); - } + for (; i < count; i++) { + await new Promise((r) => setTimeout(r, 1000)); + sendChunk({ count: i }); } return `done: ${count}, streamed: ${i} times`; } From 221b3b160c7d46c7079d5157af519003d04375d9 Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Mon, 3 Feb 2025 17:10:27 -0800 Subject: [PATCH 505/562] docs(js): Adds a guide for interrupts. (#1661) --- docs/interrupts.md | 206 +++++++++++++++++++++++++++++++++++++++++++ docs/tool-calling.md | 16 +++- 2 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 docs/interrupts.md diff --git a/docs/interrupts.md b/docs/interrupts.md new file mode 100644 index 000000000..34536c8cf --- /dev/null +++ b/docs/interrupts.md @@ -0,0 +1,206 @@ +# Interrupts + +_Interrupts_ are a special kind of [tool](tool-calling) that can pause the +LLM generation and tool calling loop to return control back to you. When +you're ready, generation can then be *resumed* with *replies* that will be +processed by the LLM for further generation. + +The most common uses for interruptions fall into a few categories: + +* **Human-in-the-Loop:** Allowing the user of an interactive AI application + to clarify needed informatino or confirm the LLM's action before it is + completed, providing a measure of safety and confidence. +* **Async Processing:** Starting an asynchronous task that can only be + completed out-of-band, such as sending an approval notification to + a human reviewer or kicking off a long-running background process. +* **Exiting Autonomous Task:** In workflows that might iterate through + a long series of tool calls, an interrupt can provide the model a way + to mark the task as complete. + +## Before you begin {:#you-begin} + +If you want to run the code examples on this page, first complete the steps in +the [Getting started](get-started) guide. All of the examples assume that you +have already set up a project with Genkit dependencies installed. + +This page discusses one of the advanced features of Genkit model abstraction, so +before you dive too deeply, you should be familiar with the content on the +[Generating content with AI models](models) page. You should also be familiar +with Genkit's system for defining input and output schemas, which is discussed +on the [Flows](flows) page and the general methods of tool calling discussed +on the [Tool Calling](tool-calling) page. + +## Overview of interrupts {:#overview-interrupts} + +At a high level, this is what an interrupt looks like when interacting with an LLM: + +1. The calling application prompts the LLM with a request and also includes in + the prompt a list of tools including at least one interrupt tool that the LLM + can use to generate a response. +2. The LLM either generates a complete response or generates a tool call request + in a specific format. To the LLM, an interrupt looks like any other tool call. +3. If the LLM selects the interrupt among the tool calls it generates, the Genkit + library will automatically halt generation rather than immediately passing + responses back to the model for additional processing. +4. The developer checks if an interrupt is called and performs whatever task is + needed to collect the information needed for the interrupt reply. +5. The developer resumes generation by passing an interrupt reply to the model, + returning to Step 2. + +## Triggering interrupts with Genkit {:#tool-calling} + +Interrupts can be triggered from any tool or by using the `defineInterrupt` method. + +### Defining interrupts + +The most common kind of interrupt is providing a tool that allows the LLM to +request clarification from the user, for example by asking a multiple choice +question. + +For this use case, use the Genkit instance's `defineInterrupt()` method: + +```ts +import { genkit, z } from 'genkit'; +import { googleAI, gemini15Flash } from '@genkitai/google-ai'; + +const ai = genkit({ + plugins: [googleAI()], + model: gemini15Flash, +}); + +const askQuestion = ai.defineInterrupt({ + name: 'askQuestion', + description: 'use this to ask the user a clarifying question', + inputSchema: z.object({ + choices: z.array(z.string()).describe('the choices to display to the user'), + allowOther: z.boolean().optional().describe('when true, allow write-ins') + }), + replySchema: z.string() +}); +``` + +Note that interrupts have a `replySchema` instead of an output schema, although +they are treated as equivalent when passing data to the model. + +### Using interrupts + +Interrupts are passed into the `tools` array when generating content, just like +other types of tools. You can pass both normal tools and interrupts to the same +generate call: + +* {Generate} + + ```ts + const response = await ai.generate({ + prompt: 'Ask me a movie trivia question.', + tools: [askQuestion], + }); + ``` + +* {definePrompt} + + ```ts + const triviaPrompt = ai.definePrompt( + { + name: 'triviaPrompt', + tools: [askQuestion], + input: { + schema: z.object({subject: z.string()}) + }, + prompt: 'Ask me a trivia question about {{subject}}.', + } + ); + + const response = await triviaPrompt({ subject: 'computer history' }); + ``` + +* {Prompt file} + + ```none + --- + tools: [askQuestion] + input: + schema: + partyType: string + --- + {{role "system}} + Use the askQuestion tool if you need to clarify something. + + {{role "user"}} + Help me plan a {{partyType}} party next week. + ``` + + Then you can execute the prompt in your code as follows: + + ```ts + // assuming prompt file is named partyPlanner.prompt + const partyPlanner = ai.prompt('partyPlanner'); + + const response = await partyPlanner({ partyType: 'birthday' }); + ``` + +* {Chat} + + ```ts + const chat = ai.chat({ + system: 'Use the askQuestion tool if you need to clarify something.', + tools: [askQuestion], + }); + + const response = await chat.send('make a plan for my birthday party'); + ``` + +Genkit will immediately return a response once an interrupt tool is triggered. + +### Replying to interrupts + +If you've passed one or more interrupt tools to your generate call, you will +need to check the response for interrupts so that you can handle them: + +```ts +// you can check the 'finishReason' of the response +response.finishReason === 'interrupted' +// or you can check to see if any interrupt requests are on the response +response.interrupts.length > 0 +``` + +Replying to an interrupt is done using the `resume` option on a subsequent +generate call, making sure to pass in the existing history. Each tool has +a `.reply()` method on it to help construct the reply. + +Once resumed, the model will re-enter the generation loop including tool +execution until it either completes or another interrupt is triggered: + +```ts +let response = await ai.generate({ + tools: [askQuestion], + system: 'ask clarifying questions until you have a complete solution', + prompt: 'help me plan a backyard BBQ', +}); + +while (response.interrupts.length) { + const answers = []; + // multiple interrupts can be called at once, so we handle them all + for (const question in response.interrupts) { + answers.push( + // use the 'reply' method on our tool to populate answers + askQuestion.reply( + question, + // send the tool request input to the user to respond + await askUser(question.toolRequest.input) + ) + ); + } + + response = await ai.generate({ + tools: [askQuestion], + messages: response.messages, + resume: { + reply: answers + } + }) +} + +// no more interrupts, we can see the final response +console.log(response.text); +``` \ No newline at end of file diff --git a/docs/tool-calling.md b/docs/tool-calling.md index 280e030e1..97382c1e8 100644 --- a/docs/tool-calling.md +++ b/docs/tool-calling.md @@ -196,10 +196,22 @@ Include defined tools in your prompts to generate content. Genkit will automatically handle the tool call if the LLM needs to use the `getWeather` tool to answer the prompt. -### Explicitly handling tool calls +### Pausing the tool loop with interrupts By default, Genkit repeatedly calls the LLM until every tool call has been -resolved. If you want more control over this tool calling loop, for example to +resolved. In some situations you may wish to conditionally pause execution to: + +- ask the user a question or display UI +- confirm a potentially risky action with the user +- request out-of-band approval for an action + +**Interrupts** are special tools that can halt the loop and return control +to your code so that you can handle more advanced scenarios. Visit the +[interrupts guide](interrupts) to learn how to use them. + +### Explicitly handling tool calls + +If you want full control over this tool calling loop, for example to apply more complicated logic, set the `returnToolRequests` parameter to `true`. Now it's your responsibility to ensure all of the tool requests are fulfilled: From a6c78813aeabcd6183b62cb0e59df170dd900f9a Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Tue, 4 Feb 2025 01:17:49 +0000 Subject: [PATCH 506/562] fix: use env var to track telemetryServerUrl (#1776) --- genkit-tools/cli/src/commands/start.ts | 81 +++++++++++++++----------- js/core/src/reflection.ts | 10 +++- js/core/src/tracing.ts | 8 ++- 3 files changed, 61 insertions(+), 38 deletions(-) diff --git a/genkit-tools/cli/src/commands/start.ts b/genkit-tools/cli/src/commands/start.ts index be1cb8919..4cf4e67f8 100644 --- a/genkit-tools/cli/src/commands/start.ts +++ b/genkit-tools/cli/src/commands/start.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { RuntimeManager } from '@genkit-ai/tools-common/manager'; import { startServer } from '@genkit-ai/tools-common/server'; import { logger } from '@genkit-ai/tools-common/utils'; import { spawn } from 'child_process'; @@ -35,36 +36,8 @@ export const start = new Command('start') .option('-p, --port ', 'port for the Dev UI') .option('-o, --open', 'Open the browser on UI start up') .action(async (options: RunOptions) => { - let runtimePromise = Promise.resolve(); - if (start.args.length > 0) { - runtimePromise = new Promise((urlResolver, reject) => { - const appProcess = spawn(start.args[0], start.args.slice(1), { - env: { ...process.env, GENKIT_ENV: 'dev' }, - shell: process.platform === 'win32', - }); - - const originalStdIn = process.stdin; - appProcess.stderr?.pipe(process.stderr); - appProcess.stdout?.pipe(process.stdout); - process.stdin?.pipe(appProcess.stdin); - - appProcess.on('error', (error): void => { - console.log(`Error in app process: ${error}`); - reject(error); - process.exitCode = 1; - }); - appProcess.on('exit', (code) => { - process.stdin?.pipe(originalStdIn); - if (code === 0) { - urlResolver(undefined); - } else { - reject(new Error(`app process exited with code ${code}`)); - } - }); - }); - } - - let uiPromise = Promise.resolve(); + // Always start the manager. + let managerPromise: Promise = startManager(true); if (!options.noui) { let port: number; if (options.port) { @@ -76,12 +49,52 @@ export const start = new Command('start') } else { port = await getPort({ port: makeRange(4000, 4099) }); } - uiPromise = startManager(true).then((manager) => - startServer(manager, port) - ); + managerPromise = managerPromise.then((manager) => { + startServer(manager, port); + return manager; + }); if (options.open) { open(`http://localhost:${port}`); } } - await Promise.all([runtimePromise, uiPromise]); + await managerPromise.then((manager: RuntimeManager) => { + const telemetryServerUrl = manager?.telemetryServerUrl; + return startRuntime(telemetryServerUrl); + }); }); + +async function startRuntime(telemetryServerUrl?: string) { + let runtimePromise = Promise.resolve(); + if (start.args.length > 0) { + runtimePromise = new Promise((urlResolver, reject) => { + const appProcess = spawn(start.args[0], start.args.slice(1), { + env: { + ...process.env, + GENKIT_TELEMETRY_SERVER: telemetryServerUrl, + GENKIT_ENV: 'dev', + }, + shell: process.platform === 'win32', + }); + + const originalStdIn = process.stdin; + appProcess.stderr?.pipe(process.stderr); + appProcess.stdout?.pipe(process.stdout); + process.stdin?.pipe(appProcess.stdin); + + appProcess.on('error', (error): void => { + console.log(`Error in app process: ${error}`); + reject(error); + process.exitCode = 1; + }); + appProcess.on('exit', (code) => { + process.stdin?.pipe(originalStdIn); + if (code === 0) { + urlResolver(undefined); + } else { + reject(new Error(`app process exited with code ${code}`)); + } + }); + }); + } + return runtimePromise; +} diff --git a/js/core/src/reflection.ts b/js/core/src/reflection.ts index a4cc79298..5cbb4ae61 100644 --- a/js/core/src/reflection.ts +++ b/js/core/src/reflection.ts @@ -226,9 +226,13 @@ export class ReflectionServer { server.post('/api/notify', async (request, response) => { const { telemetryServerUrl, reflectionApiSpecVersion } = request.body; - if (typeof telemetryServerUrl === 'string') { - setTelemetryServerUrl(telemetryServerUrl); - logger.debug(`Connected to telemetry server on ${telemetryServerUrl}`); + if (!process.env.GENKIT_TELEMETRY_SERVER) { + if (typeof telemetryServerUrl === 'string') { + setTelemetryServerUrl(telemetryServerUrl); + logger.debug( + `Connected to telemetry server on ${telemetryServerUrl}` + ); + } } if (reflectionApiSpecVersion !== GENKIT_REFLECTION_API_SPEC_VERSION) { if ( diff --git a/js/core/src/tracing.ts b/js/core/src/tracing.ts index 217019a24..87b69283d 100644 --- a/js/core/src/tracing.ts +++ b/js/core/src/tracing.ts @@ -22,7 +22,10 @@ import { } from '@opentelemetry/sdk-trace-base'; import { logger } from './logging.js'; import { TelemetryConfig } from './telemetryTypes.js'; -import { TraceServerExporter } from './tracing/exporter.js'; +import { + TraceServerExporter, + setTelemetryServerUrl, +} from './tracing/exporter.js'; import { isDevEnv } from './utils.js'; export * from './tracing/exporter.js'; @@ -51,6 +54,9 @@ export async function ensureBasicTelemetryInstrumentation() { export async function enableTelemetry( telemetryConfig: TelemetryConfig | Promise ) { + if (process.env.GENKIT_TELEMETRY_SERVER) { + setTelemetryServerUrl(process.env.GENKIT_TELEMETRY_SERVER); + } global[instrumentationKey] = telemetryConfig instanceof Promise ? telemetryConfig : Promise.resolve(); From 0d680f71b8900ed8de511a8bf1ab7d9c5055a4e7 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:49:37 +0000 Subject: [PATCH 507/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.15 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index d48bb37e5..02f5fac8f 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 32a8bec8f7fdd4d7c845cfeb744751c9fe14601c Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:49:40 +0000 Subject: [PATCH 508/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.15 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index 39722b3f1..a98d8ae0c 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From fc8ab37ebe4dde9125f240c6246b4b9d764350e5 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:49:42 +0000 Subject: [PATCH 509/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.15 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index 9becddbc0..868a7df1b 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From e2715c70ba91c5798fc39ade71fb94c5738c19bd Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:50:31 +0000 Subject: [PATCH 510/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.15 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 1f8bf2b45..93df6def0 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From db5eee2f09f71b6be04c06012f4e8470c5e0e00a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:50:33 +0000 Subject: [PATCH 511/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.15 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index 5bfc84686..a0a69189e 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From 8e6f021e8d0c64b2d81da6d01d05b95eb55e164f Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:50:36 +0000 Subject: [PATCH 512/562] chore: bump genkit version to genkit@1.0.0-rc.15 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index bfde4897a..d9255076c 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From 06ebef5806b9a915ddb1951272bbfd3baed79b65 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:50:38 +0000 Subject: [PATCH 513/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.15 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index b1cf29787..d07d38079 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From c22c73c9df42226f61186fd210e3ae88ed1dfc94 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:50:40 +0000 Subject: [PATCH 514/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.15 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 06ea1a3da..376d479a7 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From a50f2b569d6271e06ba2be4fe0674d66c426724a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:50:43 +0000 Subject: [PATCH 515/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.15 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index f50085b42..9bcb3ea58 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From 6bc2832ba1832c7faba43670596195834fabb8cd Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:50:45 +0000 Subject: [PATCH 516/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.15 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 7eb319954..4edc1fde1 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From 2072bd2821ec36bd23c7127cdc375a095f99d3c9 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:50:48 +0000 Subject: [PATCH 517/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.15 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index 8cbf6593d..1cd71ef90 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From e1e19e5e1242ba50405c7644a377581fff70f67b Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:50:50 +0000 Subject: [PATCH 518/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.15 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 5e3e2da86..82050a34b 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From 0438f0ccbb9d8c9360e9d77d38c2427c8c14ad42 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:50:52 +0000 Subject: [PATCH 519/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.15 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index 14217980c..b261eb73a 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From aecec227533531598744d2291751ad5ebea78cf5 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:50:55 +0000 Subject: [PATCH 520/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.15 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index 2029b2882..f875aaf5c 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From de824a154a509a344eb37b1a9541e9e7b499eddd Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:50:57 +0000 Subject: [PATCH 521/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.15 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index 62c157334..ab2bca68b 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From 1c0cba25c8b65db0da3f7bd587d24d73c821fd64 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:50:59 +0000 Subject: [PATCH 522/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.15 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index e1c7b3ca7..3c7578837 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From 4c0b8a834c147b8cd171879871f56ca747ca5d5a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:51:02 +0000 Subject: [PATCH 523/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.15 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index c210464f6..8cb2d297a 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From 0013c977984deed3641943bf7209b489d1f82f49 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:51:05 +0000 Subject: [PATCH 524/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.15 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index b42e4d375..b39ae1aa9 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From 220d9b6040d0e05355aef68be15e6bfb0ad19fef Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 13:51:07 +0000 Subject: [PATCH 525/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.15 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index 8c055d8b0..0161b8851 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.14", + "version": "1.0.0-rc.15", "type": "commonjs", "scripts": { "check": "tsc", From 175bcb4cf7ba066435ddf316169c8f642568260a Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Tue, 4 Feb 2025 11:28:59 -0500 Subject: [PATCH 526/562] chore: moved generate tests into shareable spec file (for go and py) (#1718) --- go/tests/api_test.go | 2 +- js/ai/package.json | 3 +- js/ai/tests/generate/action_test.ts | 282 +++++------------- js/pnpm-lock.yaml | 134 ++++++--- tests/specs/generate.yaml | 140 +++++++++ .../reflection_api.yaml} | 0 tests/src/reflection_api_test.ts | 2 +- 7 files changed, 306 insertions(+), 257 deletions(-) create mode 100644 tests/specs/generate.yaml rename tests/{reflection_api_tests.yaml => specs/reflection_api.yaml} (100%) diff --git a/go/tests/api_test.go b/go/tests/api_test.go index 59905d24f..b1a3f271c 100644 --- a/go/tests/api_test.go +++ b/go/tests/api_test.go @@ -35,7 +35,7 @@ type test struct { const hostPort = "http://localhost:3100" func TestReflectionAPI(t *testing.T) { - filenames, err := filepath.Glob(filepath.FromSlash("../../tests/reflection_api_tests.yaml")) + filenames, err := filepath.Glob(filepath.FromSlash("../../tests/specs/reflection_api.yaml")) if err != nil { t.Fatal(err) } diff --git a/js/ai/package.json b/js/ai/package.json index a0a69189e..de4228c70 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -42,7 +42,8 @@ "rimraf": "^6.0.1", "tsup": "^8.3.5", "tsx": "^4.19.2", - "typescript": "^4.9.0" + "typescript": "^4.9.0", + "yaml": "^2.7.0" }, "types": "lib/index.d.ts", "exports": { diff --git a/js/ai/tests/generate/action_test.ts b/js/ai/tests/generate/action_test.ts index b41df3ed5..a3c89447d 100644 --- a/js/ai/tests/generate/action_test.ts +++ b/js/ai/tests/generate/action_test.ts @@ -14,239 +14,99 @@ * limitations under the License. */ +import { stripUndefinedProps, z } from '@genkit-ai/core'; import { Registry } from '@genkit-ai/core/registry'; import * as assert from 'assert'; +import { readFileSync } from 'fs'; import { beforeEach, describe, it } from 'node:test'; +import { parse } from 'yaml'; import { GenerateAction, defineGenerateAction, } from '../../src/generate/action.js'; -import { GenerateResponseChunkData } from '../../src/model.js'; -import { defineTool } from '../../src/tool.js'; import { - ProgrammableModel, - defineEchoModel, - defineProgrammableModel, -} from '../helpers.js'; - -describe('generate', () => { + GenerateActionOptionsSchema, + GenerateResponseChunkData, + GenerateResponseChunkSchema, + GenerateResponseSchema, +} from '../../src/model.js'; +import { defineTool } from '../../src/tool.js'; +import { ProgrammableModel, defineProgrammableModel } from '../helpers.js'; + +const SpecSuiteSchema = z + .object({ + tests: z.array( + z + .object({ + name: z.string(), + input: GenerateActionOptionsSchema, + streamChunks: z + .array(z.array(GenerateResponseChunkSchema)) + .optional(), + modelResponses: z.array(GenerateResponseSchema), + expectResponse: GenerateResponseSchema.optional(), + stream: z.boolean().optional(), + expectChunks: z.array(GenerateResponseChunkSchema).optional(), + }) + .strict() + ), + }) + .strict(); + +describe('spec', () => { let registry: Registry; let pm: ProgrammableModel; beforeEach(() => { registry = new Registry(); defineGenerateAction(registry); - defineEchoModel(registry); pm = defineProgrammableModel(registry); - }); - - it('registers the action', async () => { - const action = await registry.lookupAction('/util/generate'); - assert.ok(action); - }); - - it('generate simple response', async () => { - const action = (await registry.lookupAction( - '/util/generate' - )) as GenerateAction; - - const response = await action({ - model: 'echoModel', - messages: [{ role: 'user', content: [{ text: 'hi' }] }], - config: { temperature: 11 }, - }); - - assert.deepStrictEqual(response, { - custom: {}, - finishReason: 'stop', - message: { - role: 'model', - content: [ - { text: 'Echo: hi' }, - { text: '; config: {"temperature":11}' }, - ], - }, - request: { - messages: [ - { - role: 'user', - content: [{ text: 'hi' }], - }, - ], - output: {}, - tools: [], - config: { - temperature: 11, - }, - docs: undefined, - }, - usage: {}, - }); - }); - - it('should call tools', async () => { - const action = (await registry.lookupAction( - '/util/generate' - )) as GenerateAction; - defineTool( registry, { name: 'testTool', description: 'description' }, async () => 'tool called' ); - - // first response be tools call, the subsequent just text response from agent b. - let reqCounter = 0; - pm.handleResponse = async (req, sc) => { - return { - message: { - role: 'model', - content: [ - reqCounter++ === 0 - ? { - toolRequest: { - name: 'testTool', - input: {}, - ref: 'ref123', - }, - } - : { - text: req.messages - .map((m) => - m.content - .map( - (c) => - c.text || JSON.stringify(c.toolResponse?.output) - ) - .join() - ) - .join(), - }, - ], - }, - }; - }; - - const response = await action({ - model: 'programmableModel', - messages: [{ role: 'user', content: [{ text: 'hi' }] }], - tools: ['testTool'], - config: { temperature: 11 }, - }); - - assert.deepStrictEqual(response, { - custom: {}, - finishReason: undefined, - message: { - role: 'model', - content: [{ text: 'hi,,"tool called"' }], - }, - request: { - messages: [ - { - role: 'user', - content: [{ text: 'hi' }], - }, - { - content: [ - { - toolRequest: { - input: {}, - name: 'testTool', - ref: 'ref123', - }, - }, - ], - role: 'model', - }, - { - content: [ - { - toolResponse: { - name: 'testTool', - output: 'tool called', - ref: 'ref123', - }, - }, - ], - role: 'tool', - }, - ], - output: {}, - tools: [ - { - description: 'description', - inputSchema: { - $schema: 'http://json-schema.org/draft-07/schema#', - }, - name: 'testTool', - outputSchema: { - $schema: 'http://json-schema.org/draft-07/schema#', - }, - }, - ], - config: { - temperature: 11, - }, - docs: undefined, - }, - usage: {}, - }); }); - it('streams simple response', async () => { - const action = (await registry.lookupAction( - '/util/generate' - )) as GenerateAction; - - const { output, stream } = action.stream({ - model: 'echoModel', - messages: [{ role: 'user', content: [{ text: 'hi' }] }], - }); - - const chunks = [] as GenerateResponseChunkData[]; - for await (const chunk of stream) { - chunks.push(chunk); - } - - assert.deepStrictEqual(chunks, [ - { - index: 0, - role: 'model', - content: [{ text: '3' }], - }, - { - index: 0, - role: 'model', - content: [{ text: '2' }], - }, - { - index: 0, - role: 'model', - content: [{ text: '1' }], - }, - ]); - - assert.deepStrictEqual(await output, { - custom: {}, - finishReason: 'stop', - message: { - role: 'model', - content: [{ text: 'Echo: hi' }, { text: '; config: undefined' }], - }, - request: { - messages: [ - { - role: 'user', - content: [{ text: 'hi' }], - }, - ], - output: {}, - tools: [], - config: undefined, - docs: undefined, - }, - usage: {}, + SpecSuiteSchema.parse( + parse(readFileSync('../../tests/specs/generate.yaml', 'utf-8')) + ).tests.forEach((test) => { + it(test.name, async () => { + if (test.modelResponses || test.streamChunks) { + let reqCounter = 0; + pm.handleResponse = async (req, sc) => { + if (test.streamChunks && sc) { + test.streamChunks[reqCounter].forEach(sc); + } + return test.modelResponses?.[reqCounter++]!; + }; + } + const action = (await registry.lookupAction( + '/util/generate' + )) as GenerateAction; + + if (test.stream) { + const { output, stream } = action.stream(test.input); + + const chunks = [] as GenerateResponseChunkData[]; + for await (const chunk of stream) { + chunks.push(stripUndefinedProps(chunk)); + } + + assert.deepStrictEqual(chunks, test.expectChunks); + + assert.deepStrictEqual( + stripUndefinedProps(await output), + test.expectResponse + ); + } else { + const response = await action(test.input); + + assert.deepStrictEqual( + stripUndefinedProps(response), + test.expectResponse + ); + } }); }); }); diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index a2d56992b..05e8e7657 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -74,13 +74,16 @@ importers: version: 6.0.1 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 typescript: specifier: ^4.9.0 version: 4.9.5 + yaml: + specifier: ^2.7.0 + version: 2.7.0 core: dependencies: @@ -156,7 +159,7 @@ importers: version: 6.0.1 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -227,7 +230,7 @@ importers: version: 6.0.1 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -261,7 +264,7 @@ importers: version: 6.0.1 tsup: specifier: ^8.0.2 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.7.0 version: 4.19.2 @@ -292,7 +295,7 @@ importers: version: 6.0.1 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -323,7 +326,7 @@ importers: version: 6.0.1 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -360,7 +363,7 @@ importers: version: 6.0.1 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -406,7 +409,7 @@ importers: version: 6.0.1 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -470,7 +473,7 @@ importers: version: 29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)))(typescript@4.9.5) tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -555,7 +558,7 @@ importers: version: 29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)))(typescript@4.9.5) tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -589,7 +592,7 @@ importers: version: 6.0.1 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -626,7 +629,7 @@ importers: version: 6.0.1 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -666,7 +669,7 @@ importers: version: 29.2.5(@babel/core@7.25.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.7))(jest@29.7.0(@types/node@20.16.9)(ts-node@10.9.2(@types/node@20.16.9)(typescript@5.6.3)))(typescript@5.6.3) tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -702,7 +705,7 @@ importers: version: 6.0.1 tsup: specifier: ^8.0.2 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.7.0 version: 4.19.2 @@ -733,7 +736,7 @@ importers: version: 6.0.1 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -764,7 +767,7 @@ importers: version: 6.0.1 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -826,7 +829,7 @@ importers: version: 6.0.1 tsup: specifier: ^8.3.5 - version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1) + version: 8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -1373,7 +1376,7 @@ importers: version: link:../../plugins/ollama genkitx-openai: specifier: ^0.10.1 - version: 0.10.1(@genkit-ai/ai@1.0.0-rc.14)(@genkit-ai/core@1.0.0-rc.14) + version: 0.10.1(@genkit-ai/ai@1.0.0-rc.15)(@genkit-ai/core@1.0.0-rc.15) devDependencies: rimraf: specifier: ^6.0.1 @@ -2505,11 +2508,11 @@ packages: '@firebase/webchannel-wrapper@1.0.3': resolution: {integrity: sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==} - '@genkit-ai/ai@1.0.0-rc.14': - resolution: {integrity: sha512-5CHyRixumiM5uHkzwHl7IWbrYSzWvhDvYAsMoq9mmI8iCJA9g/3u8JOcHSRiseuaap5gv/QGm809IpIlak55ZA==} + '@genkit-ai/ai@1.0.0-rc.15': + resolution: {integrity: sha512-cCB0Lp1Q7umV3fAKvfydz99CEeSq8n5KBfp+3oAIwFWgRvRTBgw0Y5tLtuis4TptCD2ZqqnoiedPW+3JXQU+VQ==} - '@genkit-ai/core@1.0.0-rc.14': - resolution: {integrity: sha512-8h0Vl3SCP0vYIN60uELaZWEAP6BjLxXTcoWv2MFn7khxblzVkMWq98IxPThHo3s5BGB+Pxnhnsywdb/ItliFUA==} + '@genkit-ai/core@1.0.0-rc.15': + resolution: {integrity: sha512-eq6nRTPELk7/wkhVXdaAgV1sVrLFgzcdiSeezTpsyhISH9gVWzcSHsNPwxdAR3K+szgo5PkomZQT6JIQKrlxsQ==} '@gerrit0/mini-shiki@1.24.4': resolution: {integrity: sha512-YEHW1QeAg6UmxEmswiQbOVEg1CW22b1XUD/lNTliOsu0LD0wqoyleFMnmbTp697QE0pcadQiR5cVtbbAPncvpw==} @@ -4699,6 +4702,10 @@ packages: resolution: {integrity: sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==} engines: {node: '>= 0.10.0'} + express@4.21.2: + resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} + engines: {node: '>= 0.10.0'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -6085,6 +6092,9 @@ packages: path-to-regexp@0.1.10: resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + path-type@3.0.0: resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} engines: {node: '>=4'} @@ -7015,13 +7025,13 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yaml@2.4.1: - resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==} + yaml@2.6.1: + resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==} engines: {node: '>= 14'} hasBin: true - yaml@2.6.1: - resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==} + yaml@2.7.0: + resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} engines: {node: '>= 14'} hasBin: true @@ -7822,9 +7832,9 @@ snapshots: '@firebase/webchannel-wrapper@1.0.3': {} - '@genkit-ai/ai@1.0.0-rc.14': + '@genkit-ai/ai@1.0.0-rc.15': dependencies: - '@genkit-ai/core': 1.0.0-rc.14 + '@genkit-ai/core': 1.0.0-rc.15 '@opentelemetry/api': 1.9.0 '@types/node': 20.16.9 colorette: 2.0.20 @@ -7836,7 +7846,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@genkit-ai/core@1.0.0-rc.14': + '@genkit-ai/core@1.0.0-rc.15': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) @@ -7851,7 +7861,7 @@ snapshots: body-parser: 1.20.3 cors: 2.8.5 dotprompt: 1.0.0-dev.3 - express: 4.21.1 + express: 4.21.2 get-port: 5.1.0 json-schema: 0.4.0 zod: 3.24.1 @@ -10052,7 +10062,7 @@ snapshots: dotprompt@1.0.0-dev.3: dependencies: handlebars: 4.7.8 - yaml: 2.6.1 + yaml: 2.7.0 duplexify@4.1.3: dependencies: @@ -10349,6 +10359,42 @@ snapshots: transitivePeerDependencies: - supports-color + express@4.21.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.3 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.1 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.13.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.0 + serve-static: 1.16.2 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + extend@3.0.2: {} farmhash-modern@1.1.0: {} @@ -10578,10 +10624,10 @@ snapshots: - encoding - supports-color - genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-rc.14)(@genkit-ai/core@1.0.0-rc.14): + genkitx-openai@0.10.1(@genkit-ai/ai@1.0.0-rc.15)(@genkit-ai/core@1.0.0-rc.15): dependencies: - '@genkit-ai/ai': 1.0.0-rc.14 - '@genkit-ai/core': 1.0.0-rc.14 + '@genkit-ai/ai': 1.0.0-rc.15 + '@genkit-ai/core': 1.0.0-rc.15 openai: 4.53.0(encoding@0.1.13) zod: 3.24.1 transitivePeerDependencies: @@ -11596,7 +11642,7 @@ snapshots: openapi-types: 12.1.3 p-retry: 4.6.2 uuid: 9.0.1 - yaml: 2.4.1 + yaml: 2.7.0 zod: 3.24.1 zod-to-json-schema: 3.22.5(zod@3.24.1) optionalDependencies: @@ -12129,6 +12175,8 @@ snapshots: path-to-regexp@0.1.10: {} + path-to-regexp@0.1.12: {} + path-type@3.0.0: dependencies: pify: 3.0.0 @@ -12195,13 +12243,13 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-load-config@6.0.1(postcss@8.4.47)(tsx@4.19.2)(yaml@2.6.1): + postcss-load-config@6.0.1(postcss@8.4.47)(tsx@4.19.2)(yaml@2.7.0): dependencies: lilconfig: 3.1.2 optionalDependencies: postcss: 8.4.47 tsx: 4.19.2 - yaml: 2.6.1 + yaml: 2.7.0 postcss@8.4.31: dependencies: @@ -12907,7 +12955,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.6.1): + tsup@8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@4.9.5)(yaml@2.7.0): dependencies: bundle-require: 5.0.0(esbuild@0.24.0) cac: 6.7.14 @@ -12917,7 +12965,7 @@ snapshots: esbuild: 0.24.0 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(postcss@8.4.47)(tsx@4.19.2)(yaml@2.6.1) + postcss-load-config: 6.0.1(postcss@8.4.47)(tsx@4.19.2)(yaml@2.7.0) resolve-from: 5.0.0 rollup: 4.25.0 source-map: 0.8.0-beta.0 @@ -12934,7 +12982,7 @@ snapshots: - tsx - yaml - tsup@8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.6.1): + tsup@8.3.5(postcss@8.4.47)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0): dependencies: bundle-require: 5.0.0(esbuild@0.24.0) cac: 6.7.14 @@ -12944,7 +12992,7 @@ snapshots: esbuild: 0.24.0 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(postcss@8.4.47)(tsx@4.19.2)(yaml@2.6.1) + postcss-load-config: 6.0.1(postcss@8.4.47)(tsx@4.19.2)(yaml@2.7.0) resolve-from: 5.0.0 rollup: 4.25.0 source-map: 0.8.0-beta.0 @@ -13231,10 +13279,10 @@ snapshots: yallist@4.0.0: {} - yaml@2.4.1: {} - yaml@2.6.1: {} + yaml@2.7.0: {} + yargs-parser@21.1.1: {} yargs@17.7.2: diff --git a/tests/specs/generate.yaml b/tests/specs/generate.yaml new file mode 100644 index 000000000..9899307ae --- /dev/null +++ b/tests/specs/generate.yaml @@ -0,0 +1,140 @@ +# This file describes the responses of /util/generate action + +tests: + - name: simple generate call + input: + { + model: 'programmableModel', + messages: [{ role: 'user', content: [{ text: 'hi' }] }], + config: { temperature: 11 }, + } + modelResponses: + - { + finishReason: 'stop', + message: { role: 'model', content: [{ text: 'final response' }] }, + } + expectResponse: + { + custom: {}, + finishReason: 'stop', + message: { role: 'model', content: [{ text: 'final response' }] }, + request: + { + messages: [{ role: 'user', content: [{ text: 'hi' }] }], + output: {}, + tools: [], + config: { temperature: 11 }, + }, + usage: {}, + } + - name: stream responses + stream: true + input: + { + model: 'programmableModel', + messages: [{ role: 'user', content: [{ text: 'hi' }] }], + config: { temperature: 11 }, + } + streamChunks: + - [ + { index: 0, role: 'model', content: [{ text: '3' }] }, + { index: 0, role: 'model', content: [{ text: '2' }] }, + { index: 0, role: 'model', content: [{ text: '1' }] }, + ] + modelResponses: + - { + finishReason: 'stop', + message: { role: 'model', content: [{ text: 'final response' }] }, + } + expectChunks: + [ + { index: 0, role: 'model', content: [{ text: '3' }] }, + { index: 0, role: 'model', content: [{ text: '2' }] }, + { index: 0, role: 'model', content: [{ text: '1' }] }, + ] + expectResponse: + { + custom: {}, + finishReason: 'stop', + message: { role: 'model', content: [{ text: 'final response' }] }, + request: + { + messages: [{ role: 'user', content: [{ text: 'hi' }] }], + output: {}, + tools: [], + config: { temperature: 11 }, + }, + usage: {}, + } + - name: calls tools + input: + { + model: 'programmableModel', + messages: [{ role: 'user', content: [{ text: 'hi' }] }], + config: { temperature: 11 }, + tools: ['testTool'], + } + modelResponses: + - { + message: + { + role: 'model', + content: + [ + { + toolRequest: { name: 'testTool', input: {}, ref: 'ref123' }, + }, + ], + }, + } + - { message: { role: 'model', content: [{ text: 'final response' }] } } + expectResponse: + { + custom: {}, + message: { role: 'model', content: [{ text: 'final response' }] }, + request: + { + messages: + [ + { role: 'user', content: [{ text: 'hi' }] }, + { + role: 'model', + content: + [ + { + toolRequest: + { input: {}, name: 'testTool', ref: 'ref123' }, + }, + ], + }, + { + role: 'tool', + content: + [ + { + toolResponse: + { + name: 'testTool', + output: 'tool called', + ref: 'ref123', + }, + }, + ], + }, + ], + output: {}, + tools: + [ + { + description: 'description', + inputSchema: + { $schema: 'http://json-schema.org/draft-07/schema#' }, + name: 'testTool', + outputSchema: + { $schema: 'http://json-schema.org/draft-07/schema#' }, + }, + ], + config: { temperature: 11 }, + }, + usage: {}, + } diff --git a/tests/reflection_api_tests.yaml b/tests/specs/reflection_api.yaml similarity index 100% rename from tests/reflection_api_tests.yaml rename to tests/specs/reflection_api.yaml diff --git a/tests/src/reflection_api_test.ts b/tests/src/reflection_api_test.ts index 7f16506de..50234568e 100644 --- a/tests/src/reflection_api_test.ts +++ b/tests/src/reflection_api_test.ts @@ -50,7 +50,7 @@ async function testReflectionApi() { } ); - const t = yaml.parse(readFileSync('reflection_api_tests.yaml', 'utf8')); + const t = yaml.parse(readFileSync('specs/reflection_api.yaml', 'utf8')); for (const test of t.tests) { console.log('path', test.path); let fetchopts = { From c1f654a6a64becbd909f194da474af854637ed3d Mon Sep 17 00:00:00 2001 From: Anthony Barone Date: Tue, 4 Feb 2025 11:53:23 -0500 Subject: [PATCH 527/562] chore: update js samples to 1.0.0-rc.15 (#1824) --- .../js-chatbot/genkit-app/package-lock.json | 32 ++++---- samples/js-chatbot/genkit-app/package.json | 2 +- samples/js-chatbot/package-lock.json | 32 ++++---- samples/js-chatbot/package.json | 2 +- samples/js-chatbot/server/package-lock.json | 80 +++++++++---------- samples/js-chatbot/server/package.json | 8 +- samples/js-prompts/package-lock.json | 78 +++++++++--------- samples/js-prompts/package.json | 8 +- samples/js-schoolAgent/package-lock.json | 70 ++++++++-------- samples/js-schoolAgent/package.json | 6 +- 10 files changed, 159 insertions(+), 159 deletions(-) diff --git a/samples/js-chatbot/genkit-app/package-lock.json b/samples/js-chatbot/genkit-app/package-lock.json index c0abeeebd..0810d54c9 100644 --- a/samples/js-chatbot/genkit-app/package-lock.json +++ b/samples/js-chatbot/genkit-app/package-lock.json @@ -18,7 +18,7 @@ "@angular/platform-browser": "^18.0.0", "@angular/platform-browser-dynamic": "^18.0.0", "@angular/router": "^18.0.0", - "genkit": "^1.0.0-rc.14", + "genkit": "^1.0.0-rc.15", "marked": "^12.0.2", "ngx-markdown": "^18.0.0", "prismjs": "^1.29.0", @@ -2836,11 +2836,11 @@ } }, "node_modules/@genkit-ai/ai": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.14.tgz", - "integrity": "sha512-5CHyRixumiM5uHkzwHl7IWbrYSzWvhDvYAsMoq9mmI8iCJA9g/3u8JOcHSRiseuaap5gv/QGm809IpIlak55ZA==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.15.tgz", + "integrity": "sha512-cCB0Lp1Q7umV3fAKvfydz99CEeSq8n5KBfp+3oAIwFWgRvRTBgw0Y5tLtuis4TptCD2ZqqnoiedPW+3JXQU+VQ==", "dependencies": { - "@genkit-ai/core": "1.0.0-rc.14", + "@genkit-ai/core": "1.0.0-rc.15", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.11.19", "colorette": "^2.0.20", @@ -2852,9 +2852,9 @@ } }, "node_modules/@genkit-ai/ai/node_modules/@types/node": { - "version": "20.17.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", - "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", + "version": "20.17.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.17.tgz", + "integrity": "sha512-/WndGO4kIfMicEQLTi/mDANUu/iVUhT7KboZPdEqqHQ4aTS+3qT3U5gIqWDFV+XouorjfgGqvKILJeHhuQgFYg==", "dependencies": { "undici-types": "~6.19.2" } @@ -2865,9 +2865,9 @@ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/@genkit-ai/core": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.14.tgz", - "integrity": "sha512-8h0Vl3SCP0vYIN60uELaZWEAP6BjLxXTcoWv2MFn7khxblzVkMWq98IxPThHo3s5BGB+Pxnhnsywdb/ItliFUA==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.15.tgz", + "integrity": "sha512-eq6nRTPELk7/wkhVXdaAgV1sVrLFgzcdiSeezTpsyhISH9gVWzcSHsNPwxdAR3K+szgo5PkomZQT6JIQKrlxsQ==", "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -8888,12 +8888,12 @@ } }, "node_modules/genkit": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.14.tgz", - "integrity": "sha512-JB6fTcJ5CI4UhsP4uNyXpi9/OatyhPwOkBXnbHafE5rdibXuKUXadbb2LC+PWfG3+7WrLXGKlrj7kOIN3Hq2lw==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.15.tgz", + "integrity": "sha512-2dFUQ4qnXxv98ijs8/dr3yjof7Fev+FT+alxuRwXxUhpqGc4ei/0p1xgtFeq5j6sWBoRcUHNvzCPrMwgDDd4pA==", "dependencies": { - "@genkit-ai/ai": "1.0.0-rc.14", - "@genkit-ai/core": "1.0.0-rc.14", + "@genkit-ai/ai": "1.0.0-rc.15", + "@genkit-ai/core": "1.0.0-rc.15", "uuid": "^10.0.0" } }, diff --git a/samples/js-chatbot/genkit-app/package.json b/samples/js-chatbot/genkit-app/package.json index 1dd0c3c26..1f07c1890 100644 --- a/samples/js-chatbot/genkit-app/package.json +++ b/samples/js-chatbot/genkit-app/package.json @@ -26,7 +26,7 @@ "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3", - "genkit": "^1.0.0-rc.14" + "genkit": "^1.0.0-rc.15" }, "devDependencies": { "@angular-devkit/build-angular": "^18.0.2", diff --git a/samples/js-chatbot/package-lock.json b/samples/js-chatbot/package-lock.json index f5109a5c8..dad19b021 100644 --- a/samples/js-chatbot/package-lock.json +++ b/samples/js-chatbot/package-lock.json @@ -10,7 +10,7 @@ "license": "ISC", "devDependencies": { "concurrently": "^8.2.2", - "genkit-cli": "^1.0.0-rc.14" + "genkit-cli": "^1.0.0-rc.15" } }, "node_modules/@asteasolutions/zod-to-openapi": { @@ -442,13 +442,13 @@ } }, "node_modules/@genkit-ai/telemetry-server": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.14.tgz", - "integrity": "sha512-cFxLS8IkXhRds9+wgaru3QirgehQImG9CuhLfjo09MRcIM21Vr9F083YVnlO8WDckWz5NwclkfFKkD25FDpE/w==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.15.tgz", + "integrity": "sha512-pJhT1JUYBPsmHfRjrJ0WIzh1n78haDDVlyEZmGdM3Tr338yXxQB/mJhKI7sUEpu2O/uerxOI1kzNxPobTPjmjQ==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", - "@genkit-ai/tools-common": "1.0.0-rc.14", + "@genkit-ai/tools-common": "1.0.0-rc.15", "@google-cloud/firestore": "^7.6.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -462,9 +462,9 @@ } }, "node_modules/@genkit-ai/tools-common": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.14.tgz", - "integrity": "sha512-50PE/CIL3u9YC4c3vifz6CVSlC1cmQ3xAHMOsUxsRRvcPzIbL8jOCYMKAnbdgAkp0WGgWG+fLjiKbUPZydEgcg==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.15.tgz", + "integrity": "sha512-/kYSlspK/3Z1BbFMXjkqCYW85HVBVYnupJeRZYSIs2kEer/GJU1epiSXBo8K70w1TrC3GK9t2CSh2cILW9PvLg==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", @@ -3068,13 +3068,13 @@ } }, "node_modules/genkit-cli": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.14.tgz", - "integrity": "sha512-JR8Vvm2rGGQMsBkq+dWSypUC4ij1Uew5cjFaS74flyv/glV6RMNMuXP5XQBc5C9zIkfXeT+kng8BghHM4Gx/4g==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.15.tgz", + "integrity": "sha512-OADTCKQ97MUWBPsS2NknSQWQjQ1FKhCiFa/BXFRchib1k3gC4bYtAVjRADPg/jn1STjNuOmCblTru6/IDiUaEw==", "dev": true, "dependencies": { - "@genkit-ai/telemetry-server": "1.0.0-rc.14", - "@genkit-ai/tools-common": "1.0.0-rc.14", + "@genkit-ai/telemetry-server": "1.0.0-rc.15", + "@genkit-ai/tools-common": "1.0.0-rc.15", "axios": "^1.7.7", "colorette": "^2.0.20", "commander": "^11.1.0", @@ -4527,9 +4527,9 @@ "dev": true }, "node_modules/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "bin": { "semver": "bin/semver.js" diff --git a/samples/js-chatbot/package.json b/samples/js-chatbot/package.json index db5de649c..bc42f5a2e 100644 --- a/samples/js-chatbot/package.json +++ b/samples/js-chatbot/package.json @@ -13,6 +13,6 @@ "license": "ISC", "devDependencies": { "concurrently": "^8.2.2", - "genkit-cli": "^1.0.0-rc.14" + "genkit-cli": "^1.0.0-rc.15" } } diff --git a/samples/js-chatbot/server/package-lock.json b/samples/js-chatbot/server/package-lock.json index 574af6e28..e299da682 100644 --- a/samples/js-chatbot/server/package-lock.json +++ b/samples/js-chatbot/server/package-lock.json @@ -9,15 +9,15 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@genkit-ai/express": "^1.0.0-rc.14", - "@genkit-ai/vertexai": "^1.0.0-rc.14", + "@genkit-ai/express": "^1.0.0-rc.15", + "@genkit-ai/vertexai": "^1.0.0-rc.15", "express": "^4.21.0", - "genkit": "^1.0.0-rc.14", + "genkit": "^1.0.0-rc.15", "partial-json": "^0.1.7", "zod": "^3.23.8" }, "devDependencies": { - "genkit-cli": "^1.0.0-rc.14", + "genkit-cli": "^1.0.0-rc.15", "tsx": "^4.19.2", "typescript": "^5.4.5" } @@ -588,11 +588,11 @@ } }, "node_modules/@genkit-ai/ai": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.14.tgz", - "integrity": "sha512-5CHyRixumiM5uHkzwHl7IWbrYSzWvhDvYAsMoq9mmI8iCJA9g/3u8JOcHSRiseuaap5gv/QGm809IpIlak55ZA==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.15.tgz", + "integrity": "sha512-cCB0Lp1Q7umV3fAKvfydz99CEeSq8n5KBfp+3oAIwFWgRvRTBgw0Y5tLtuis4TptCD2ZqqnoiedPW+3JXQU+VQ==", "dependencies": { - "@genkit-ai/core": "1.0.0-rc.14", + "@genkit-ai/core": "1.0.0-rc.15", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.11.19", "colorette": "^2.0.20", @@ -604,9 +604,9 @@ } }, "node_modules/@genkit-ai/ai/node_modules/@types/node": { - "version": "20.17.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", - "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", + "version": "20.17.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.17.tgz", + "integrity": "sha512-/WndGO4kIfMicEQLTi/mDANUu/iVUhT7KboZPdEqqHQ4aTS+3qT3U5gIqWDFV+XouorjfgGqvKILJeHhuQgFYg==", "dependencies": { "undici-types": "~6.19.2" } @@ -629,9 +629,9 @@ } }, "node_modules/@genkit-ai/core": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.14.tgz", - "integrity": "sha512-8h0Vl3SCP0vYIN60uELaZWEAP6BjLxXTcoWv2MFn7khxblzVkMWq98IxPThHo3s5BGB+Pxnhnsywdb/ItliFUA==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.15.tgz", + "integrity": "sha512-eq6nRTPELk7/wkhVXdaAgV1sVrLFgzcdiSeezTpsyhISH9gVWzcSHsNPwxdAR3K+szgo5PkomZQT6JIQKrlxsQ==", "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -654,26 +654,26 @@ } }, "node_modules/@genkit-ai/express": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/express/-/express-1.0.0-rc.14.tgz", - "integrity": "sha512-I8VcuV2iyJXs/X3nCQZ/UR+FIO7XwaBo6+KWSDnugJrCweQZQ0LCYHbinSp1obLm/xCML7CWnqpZTX87mnbe3w==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/express/-/express-1.0.0-rc.15.tgz", + "integrity": "sha512-O5O5i1dVetBIu5cFQJhOjp+Xjt65hXzzJaZUGNQ3InOKB8obNltetQGPFNn+euTVfPsMXOZbZhTIXAXprwu70A==", "dependencies": { "body-parser": "^1.20.3", "cors": "^2.8.5" }, "peerDependencies": { "express": "^4.21.1", - "genkit": "^1.0.0-rc.14" + "genkit": "^1.0.0-rc.15" } }, "node_modules/@genkit-ai/telemetry-server": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.14.tgz", - "integrity": "sha512-cFxLS8IkXhRds9+wgaru3QirgehQImG9CuhLfjo09MRcIM21Vr9F083YVnlO8WDckWz5NwclkfFKkD25FDpE/w==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.15.tgz", + "integrity": "sha512-pJhT1JUYBPsmHfRjrJ0WIzh1n78haDDVlyEZmGdM3Tr338yXxQB/mJhKI7sUEpu2O/uerxOI1kzNxPobTPjmjQ==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", - "@genkit-ai/tools-common": "1.0.0-rc.14", + "@genkit-ai/tools-common": "1.0.0-rc.15", "@google-cloud/firestore": "^7.6.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -687,9 +687,9 @@ } }, "node_modules/@genkit-ai/tools-common": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.14.tgz", - "integrity": "sha512-50PE/CIL3u9YC4c3vifz6CVSlC1cmQ3xAHMOsUxsRRvcPzIbL8jOCYMKAnbdgAkp0WGgWG+fLjiKbUPZydEgcg==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.15.tgz", + "integrity": "sha512-/kYSlspK/3Z1BbFMXjkqCYW85HVBVYnupJeRZYSIs2kEer/GJU1epiSXBo8K70w1TrC3GK9t2CSh2cILW9PvLg==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", @@ -720,14 +720,14 @@ } }, "node_modules/@genkit-ai/vertexai": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/vertexai/-/vertexai-1.0.0-rc.14.tgz", - "integrity": "sha512-7j3xV9wdqIhjUjvy6cogIaTATl28z+wjiP9hLfeYb6XOyG7HFve5jZ4xzDHK2pGVOjblrP2XDyahOE2BUK6fyQ==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/vertexai/-/vertexai-1.0.0-rc.15.tgz", + "integrity": "sha512-HAGZYkKsmGuJqi3qda41hEwE25xlyz8J9kZaaevN4/RdmNWgLSE+YqUkfX/e6vPjq25/dp6a7d38UrpkfjXlgA==", "dependencies": { "@anthropic-ai/sdk": "^0.24.3", "@anthropic-ai/vertex-sdk": "^0.4.0", "@google-cloud/aiplatform": "^3.23.0", - "@google-cloud/vertexai": "^1.9.2", + "@google-cloud/vertexai": "^1.9.3", "@mistralai/mistralai-gcp": "^1.3.5", "google-auth-library": "^9.14.2", "googleapis": "^140.0.1", @@ -739,7 +739,7 @@ "firebase-admin": ">=12.2" }, "peerDependencies": { - "genkit": "^1.0.0-rc.14" + "genkit": "^1.0.0-rc.15" } }, "node_modules/@google-cloud/aiplatform": { @@ -3648,23 +3648,23 @@ } }, "node_modules/genkit": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.14.tgz", - "integrity": "sha512-JB6fTcJ5CI4UhsP4uNyXpi9/OatyhPwOkBXnbHafE5rdibXuKUXadbb2LC+PWfG3+7WrLXGKlrj7kOIN3Hq2lw==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.15.tgz", + "integrity": "sha512-2dFUQ4qnXxv98ijs8/dr3yjof7Fev+FT+alxuRwXxUhpqGc4ei/0p1xgtFeq5j6sWBoRcUHNvzCPrMwgDDd4pA==", "dependencies": { - "@genkit-ai/ai": "1.0.0-rc.14", - "@genkit-ai/core": "1.0.0-rc.14", + "@genkit-ai/ai": "1.0.0-rc.15", + "@genkit-ai/core": "1.0.0-rc.15", "uuid": "^10.0.0" } }, "node_modules/genkit-cli": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.14.tgz", - "integrity": "sha512-JR8Vvm2rGGQMsBkq+dWSypUC4ij1Uew5cjFaS74flyv/glV6RMNMuXP5XQBc5C9zIkfXeT+kng8BghHM4Gx/4g==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.15.tgz", + "integrity": "sha512-OADTCKQ97MUWBPsS2NknSQWQjQ1FKhCiFa/BXFRchib1k3gC4bYtAVjRADPg/jn1STjNuOmCblTru6/IDiUaEw==", "dev": true, "dependencies": { - "@genkit-ai/telemetry-server": "1.0.0-rc.14", - "@genkit-ai/tools-common": "1.0.0-rc.14", + "@genkit-ai/telemetry-server": "1.0.0-rc.15", + "@genkit-ai/tools-common": "1.0.0-rc.15", "axios": "^1.7.7", "colorette": "^2.0.20", "commander": "^11.1.0", diff --git a/samples/js-chatbot/server/package.json b/samples/js-chatbot/server/package.json index 2dafe7673..4601f055e 100644 --- a/samples/js-chatbot/server/package.json +++ b/samples/js-chatbot/server/package.json @@ -15,15 +15,15 @@ "author": "", "license": "ISC", "dependencies": { - "genkit": "^1.0.0-rc.14", - "@genkit-ai/vertexai": "^1.0.0-rc.14", - "@genkit-ai/express": "^1.0.0-rc.14", + "genkit": "^1.0.0-rc.15", + "@genkit-ai/vertexai": "^1.0.0-rc.15", + "@genkit-ai/express": "^1.0.0-rc.15", "express": "^4.21.0", "partial-json": "^0.1.7", "zod": "^3.23.8" }, "devDependencies": { - "genkit-cli": "^1.0.0-rc.14", + "genkit-cli": "^1.0.0-rc.15", "typescript": "^5.4.5", "tsx": "^4.19.2" } diff --git a/samples/js-prompts/package-lock.json b/samples/js-prompts/package-lock.json index e6c42dd0a..7677ccd72 100644 --- a/samples/js-prompts/package-lock.json +++ b/samples/js-prompts/package-lock.json @@ -9,14 +9,14 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@genkit-ai/express": "^1.0.0-rc.14", - "@genkit-ai/googleai": "^1.0.0-rc.14", + "@genkit-ai/express": "^1.0.0-rc.15", + "@genkit-ai/googleai": "^1.0.0-rc.15", "express": "^4.21.0", - "genkit": "^1.0.0-rc.14", + "genkit": "^1.0.0-rc.15", "zod": "^3.23.8" }, "devDependencies": { - "genkit-cli": "^1.0.0-rc.14", + "genkit-cli": "^1.0.0-rc.15", "typescript": "^5.5.4" } }, @@ -437,11 +437,11 @@ } }, "node_modules/@genkit-ai/ai": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.14.tgz", - "integrity": "sha512-5CHyRixumiM5uHkzwHl7IWbrYSzWvhDvYAsMoq9mmI8iCJA9g/3u8JOcHSRiseuaap5gv/QGm809IpIlak55ZA==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.15.tgz", + "integrity": "sha512-cCB0Lp1Q7umV3fAKvfydz99CEeSq8n5KBfp+3oAIwFWgRvRTBgw0Y5tLtuis4TptCD2ZqqnoiedPW+3JXQU+VQ==", "dependencies": { - "@genkit-ai/core": "1.0.0-rc.14", + "@genkit-ai/core": "1.0.0-rc.15", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.11.19", "colorette": "^2.0.20", @@ -453,9 +453,9 @@ } }, "node_modules/@genkit-ai/core": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.14.tgz", - "integrity": "sha512-8h0Vl3SCP0vYIN60uELaZWEAP6BjLxXTcoWv2MFn7khxblzVkMWq98IxPThHo3s5BGB+Pxnhnsywdb/ItliFUA==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.15.tgz", + "integrity": "sha512-eq6nRTPELk7/wkhVXdaAgV1sVrLFgzcdiSeezTpsyhISH9gVWzcSHsNPwxdAR3K+szgo5PkomZQT6JIQKrlxsQ==", "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -478,39 +478,39 @@ } }, "node_modules/@genkit-ai/express": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/express/-/express-1.0.0-rc.14.tgz", - "integrity": "sha512-I8VcuV2iyJXs/X3nCQZ/UR+FIO7XwaBo6+KWSDnugJrCweQZQ0LCYHbinSp1obLm/xCML7CWnqpZTX87mnbe3w==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/express/-/express-1.0.0-rc.15.tgz", + "integrity": "sha512-O5O5i1dVetBIu5cFQJhOjp+Xjt65hXzzJaZUGNQ3InOKB8obNltetQGPFNn+euTVfPsMXOZbZhTIXAXprwu70A==", "dependencies": { "body-parser": "^1.20.3", "cors": "^2.8.5" }, "peerDependencies": { "express": "^4.21.1", - "genkit": "^1.0.0-rc.14" + "genkit": "^1.0.0-rc.15" } }, "node_modules/@genkit-ai/googleai": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-1.0.0-rc.14.tgz", - "integrity": "sha512-zeEWzPhVUpi6iJ7fHS4Hn1WnwrNLyA02LNHG6f/9iaEkCQJBbV3YHHdpRA2lIXSAwhdx619KPBoFRM2JeGm9YQ==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-1.0.0-rc.15.tgz", + "integrity": "sha512-Hk+Qvm0Gd+0omRMLr9rvC0OQ8/EBmDsSGs8ir8a2YIxkAxcyxacqT+Uk99VG1YIv5APaSv0Se3zxEdTU3VGptw==", "dependencies": { "@google/generative-ai": "^0.21.0", "google-auth-library": "^9.6.3", "node-fetch": "^3.3.2" }, "peerDependencies": { - "genkit": "^1.0.0-rc.14" + "genkit": "^1.0.0-rc.15" } }, "node_modules/@genkit-ai/telemetry-server": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.14.tgz", - "integrity": "sha512-cFxLS8IkXhRds9+wgaru3QirgehQImG9CuhLfjo09MRcIM21Vr9F083YVnlO8WDckWz5NwclkfFKkD25FDpE/w==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.15.tgz", + "integrity": "sha512-pJhT1JUYBPsmHfRjrJ0WIzh1n78haDDVlyEZmGdM3Tr338yXxQB/mJhKI7sUEpu2O/uerxOI1kzNxPobTPjmjQ==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", - "@genkit-ai/tools-common": "1.0.0-rc.14", + "@genkit-ai/tools-common": "1.0.0-rc.15", "@google-cloud/firestore": "^7.6.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -524,9 +524,9 @@ } }, "node_modules/@genkit-ai/tools-common": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.14.tgz", - "integrity": "sha512-50PE/CIL3u9YC4c3vifz6CVSlC1cmQ3xAHMOsUxsRRvcPzIbL8jOCYMKAnbdgAkp0WGgWG+fLjiKbUPZydEgcg==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.15.tgz", + "integrity": "sha512-/kYSlspK/3Z1BbFMXjkqCYW85HVBVYnupJeRZYSIs2kEer/GJU1epiSXBo8K70w1TrC3GK9t2CSh2cILW9PvLg==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", @@ -3081,23 +3081,23 @@ } }, "node_modules/genkit": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.14.tgz", - "integrity": "sha512-JB6fTcJ5CI4UhsP4uNyXpi9/OatyhPwOkBXnbHafE5rdibXuKUXadbb2LC+PWfG3+7WrLXGKlrj7kOIN3Hq2lw==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.15.tgz", + "integrity": "sha512-2dFUQ4qnXxv98ijs8/dr3yjof7Fev+FT+alxuRwXxUhpqGc4ei/0p1xgtFeq5j6sWBoRcUHNvzCPrMwgDDd4pA==", "dependencies": { - "@genkit-ai/ai": "1.0.0-rc.14", - "@genkit-ai/core": "1.0.0-rc.14", + "@genkit-ai/ai": "1.0.0-rc.15", + "@genkit-ai/core": "1.0.0-rc.15", "uuid": "^10.0.0" } }, "node_modules/genkit-cli": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.14.tgz", - "integrity": "sha512-JR8Vvm2rGGQMsBkq+dWSypUC4ij1Uew5cjFaS74flyv/glV6RMNMuXP5XQBc5C9zIkfXeT+kng8BghHM4Gx/4g==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.15.tgz", + "integrity": "sha512-OADTCKQ97MUWBPsS2NknSQWQjQ1FKhCiFa/BXFRchib1k3gC4bYtAVjRADPg/jn1STjNuOmCblTru6/IDiUaEw==", "dev": true, "dependencies": { - "@genkit-ai/telemetry-server": "1.0.0-rc.14", - "@genkit-ai/tools-common": "1.0.0-rc.14", + "@genkit-ai/telemetry-server": "1.0.0-rc.15", + "@genkit-ai/tools-common": "1.0.0-rc.15", "axios": "^1.7.7", "colorette": "^2.0.20", "commander": "^11.1.0", @@ -4570,9 +4570,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, diff --git a/samples/js-prompts/package.json b/samples/js-prompts/package.json index af227f43d..88dd5a979 100644 --- a/samples/js-prompts/package.json +++ b/samples/js-prompts/package.json @@ -14,14 +14,14 @@ "author": "", "license": "ISC", "dependencies": { - "genkit": "^1.0.0-rc.14", - "@genkit-ai/googleai": "^1.0.0-rc.14", - "@genkit-ai/express": "^1.0.0-rc.14", + "genkit": "^1.0.0-rc.15", + "@genkit-ai/googleai": "^1.0.0-rc.15", + "@genkit-ai/express": "^1.0.0-rc.15", "express": "^4.21.0", "zod": "^3.23.8" }, "devDependencies": { - "genkit-cli": "^1.0.0-rc.14", + "genkit-cli": "^1.0.0-rc.15", "typescript": "^5.5.4" } } diff --git a/samples/js-schoolAgent/package-lock.json b/samples/js-schoolAgent/package-lock.json index 277dd1113..50de68add 100644 --- a/samples/js-schoolAgent/package-lock.json +++ b/samples/js-schoolAgent/package-lock.json @@ -1,6 +1,6 @@ { "name": "school-agent", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { @@ -9,8 +9,8 @@ "version": "1.0.1", "license": "ISC", "dependencies": { - "@genkit-ai/googleai": "^1.0.0-rc.14", - "genkit": "^1.0.0-rc.14", + "@genkit-ai/googleai": "^1.0.0-rc.15", + "genkit": "^1.0.0-rc.15", "google-auth-library": "^9.6.3", "llm-chunk": "^0.0.1", "pdf-parse": "^1.1.1" @@ -18,7 +18,7 @@ "devDependencies": { "@types/pdf-parse": "^1.1.4", "cross-env": "^7.0.3", - "genkit-cli": "^1.0.0-rc.14", + "genkit-cli": "^1.0.0-rc.15", "rimraf": "^6.0.1", "tsx": "^4.19.1", "typescript": "^5.3.3" @@ -441,11 +441,11 @@ } }, "node_modules/@genkit-ai/ai": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.14.tgz", - "integrity": "sha512-5CHyRixumiM5uHkzwHl7IWbrYSzWvhDvYAsMoq9mmI8iCJA9g/3u8JOcHSRiseuaap5gv/QGm809IpIlak55ZA==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.15.tgz", + "integrity": "sha512-cCB0Lp1Q7umV3fAKvfydz99CEeSq8n5KBfp+3oAIwFWgRvRTBgw0Y5tLtuis4TptCD2ZqqnoiedPW+3JXQU+VQ==", "dependencies": { - "@genkit-ai/core": "1.0.0-rc.14", + "@genkit-ai/core": "1.0.0-rc.15", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.11.19", "colorette": "^2.0.20", @@ -457,9 +457,9 @@ } }, "node_modules/@genkit-ai/core": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.14.tgz", - "integrity": "sha512-8h0Vl3SCP0vYIN60uELaZWEAP6BjLxXTcoWv2MFn7khxblzVkMWq98IxPThHo3s5BGB+Pxnhnsywdb/ItliFUA==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.15.tgz", + "integrity": "sha512-eq6nRTPELk7/wkhVXdaAgV1sVrLFgzcdiSeezTpsyhISH9gVWzcSHsNPwxdAR3K+szgo5PkomZQT6JIQKrlxsQ==", "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -482,26 +482,26 @@ } }, "node_modules/@genkit-ai/googleai": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-1.0.0-rc.14.tgz", - "integrity": "sha512-zeEWzPhVUpi6iJ7fHS4Hn1WnwrNLyA02LNHG6f/9iaEkCQJBbV3YHHdpRA2lIXSAwhdx619KPBoFRM2JeGm9YQ==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/googleai/-/googleai-1.0.0-rc.15.tgz", + "integrity": "sha512-Hk+Qvm0Gd+0omRMLr9rvC0OQ8/EBmDsSGs8ir8a2YIxkAxcyxacqT+Uk99VG1YIv5APaSv0Se3zxEdTU3VGptw==", "dependencies": { "@google/generative-ai": "^0.21.0", "google-auth-library": "^9.6.3", "node-fetch": "^3.3.2" }, "peerDependencies": { - "genkit": "^1.0.0-rc.14" + "genkit": "^1.0.0-rc.15" } }, "node_modules/@genkit-ai/telemetry-server": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.14.tgz", - "integrity": "sha512-cFxLS8IkXhRds9+wgaru3QirgehQImG9CuhLfjo09MRcIM21Vr9F083YVnlO8WDckWz5NwclkfFKkD25FDpE/w==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.15.tgz", + "integrity": "sha512-pJhT1JUYBPsmHfRjrJ0WIzh1n78haDDVlyEZmGdM3Tr338yXxQB/mJhKI7sUEpu2O/uerxOI1kzNxPobTPjmjQ==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", - "@genkit-ai/tools-common": "1.0.0-rc.14", + "@genkit-ai/tools-common": "1.0.0-rc.15", "@google-cloud/firestore": "^7.6.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -515,9 +515,9 @@ } }, "node_modules/@genkit-ai/tools-common": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.14.tgz", - "integrity": "sha512-50PE/CIL3u9YC4c3vifz6CVSlC1cmQ3xAHMOsUxsRRvcPzIbL8jOCYMKAnbdgAkp0WGgWG+fLjiKbUPZydEgcg==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.15.tgz", + "integrity": "sha512-/kYSlspK/3Z1BbFMXjkqCYW85HVBVYnupJeRZYSIs2kEer/GJU1epiSXBo8K70w1TrC3GK9t2CSh2cILW9PvLg==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", @@ -3097,23 +3097,23 @@ } }, "node_modules/genkit": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.14.tgz", - "integrity": "sha512-JB6fTcJ5CI4UhsP4uNyXpi9/OatyhPwOkBXnbHafE5rdibXuKUXadbb2LC+PWfG3+7WrLXGKlrj7kOIN3Hq2lw==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.15.tgz", + "integrity": "sha512-2dFUQ4qnXxv98ijs8/dr3yjof7Fev+FT+alxuRwXxUhpqGc4ei/0p1xgtFeq5j6sWBoRcUHNvzCPrMwgDDd4pA==", "dependencies": { - "@genkit-ai/ai": "1.0.0-rc.14", - "@genkit-ai/core": "1.0.0-rc.14", + "@genkit-ai/ai": "1.0.0-rc.15", + "@genkit-ai/core": "1.0.0-rc.15", "uuid": "^10.0.0" } }, "node_modules/genkit-cli": { - "version": "1.0.0-rc.14", - "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.14.tgz", - "integrity": "sha512-JR8Vvm2rGGQMsBkq+dWSypUC4ij1Uew5cjFaS74flyv/glV6RMNMuXP5XQBc5C9zIkfXeT+kng8BghHM4Gx/4g==", + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.15.tgz", + "integrity": "sha512-OADTCKQ97MUWBPsS2NknSQWQjQ1FKhCiFa/BXFRchib1k3gC4bYtAVjRADPg/jn1STjNuOmCblTru6/IDiUaEw==", "dev": true, "dependencies": { - "@genkit-ai/telemetry-server": "1.0.0-rc.14", - "@genkit-ai/tools-common": "1.0.0-rc.14", + "@genkit-ai/telemetry-server": "1.0.0-rc.15", + "@genkit-ai/tools-common": "1.0.0-rc.15", "axios": "^1.7.7", "colorette": "^2.0.20", "commander": "^11.1.0", @@ -4726,9 +4726,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, diff --git a/samples/js-schoolAgent/package.json b/samples/js-schoolAgent/package.json index 475483e9d..14bf057af 100644 --- a/samples/js-schoolAgent/package.json +++ b/samples/js-schoolAgent/package.json @@ -16,14 +16,14 @@ "author": "", "license": "ISC", "dependencies": { - "genkit": "^1.0.0-rc.14", - "@genkit-ai/googleai": "^1.0.0-rc.14", + "genkit": "^1.0.0-rc.15", + "@genkit-ai/googleai": "^1.0.0-rc.15", "google-auth-library": "^9.6.3", "llm-chunk": "^0.0.1", "pdf-parse": "^1.1.1" }, "devDependencies": { - "genkit-cli": "^1.0.0-rc.14", + "genkit-cli": "^1.0.0-rc.15", "@types/pdf-parse": "^1.1.4", "cross-env": "^7.0.3", "rimraf": "^6.0.1", From fa3177e9ffec5ff0c0533f5a96564f42ba71ed5e Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Tue, 4 Feb 2025 11:55:03 -0500 Subject: [PATCH 528/562] chore: update-js-chat-to-1.0 (#1823) * chore: update-js-chat-to-1.0 --- samples/js-angular/README.md | 4 +- .../js-angular/genkit-app/package-lock.json | 14454 ++++++++++++++++ samples/js-angular/genkit-app/package.json | 2 +- .../app/samples/chatbot/chatbot.component.ts | 2 +- .../streaming-json.component.ts | 2 +- samples/js-angular/server/package-lock.json | 6374 +++++++ samples/js-angular/server/package.json | 7 +- samples/js-angular/server/src/agent.ts | 5 +- samples/js-angular/server/src/index.ts | 4 +- 9 files changed, 20841 insertions(+), 13 deletions(-) create mode 100644 samples/js-angular/genkit-app/package-lock.json create mode 100644 samples/js-angular/server/package-lock.json diff --git a/samples/js-angular/README.md b/samples/js-angular/README.md index 4e02b6f32..ede90d510 100644 --- a/samples/js-angular/README.md +++ b/samples/js-angular/README.md @@ -6,7 +6,7 @@ To build: ```bash npm i -npm run build +npm run setup ``` The sample is using Vertex AI, so you'll need to auth: @@ -18,7 +18,7 @@ gcloud auth application-default login To run the sample: ```bash -npm start +npm run genkit:dev ``` Point your browser to http://localhost:4200/ diff --git a/samples/js-angular/genkit-app/package-lock.json b/samples/js-angular/genkit-app/package-lock.json new file mode 100644 index 000000000..7fb8b8194 --- /dev/null +++ b/samples/js-angular/genkit-app/package-lock.json @@ -0,0 +1,14454 @@ +{ + "name": "genkit-app", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "genkit-app", + "version": "0.0.0", + "dependencies": { + "@angular/animations": "^18.0.0", + "@angular/cdk": "^18.0.1", + "@angular/common": "^18.0.0", + "@angular/compiler": "^18.0.0", + "@angular/core": "^18.0.0", + "@angular/forms": "^18.0.0", + "@angular/material": "^18.0.1", + "@angular/platform-browser": "^18.0.0", + "@angular/platform-browser-dynamic": "^18.0.0", + "@angular/router": "^18.0.0", + "genkit": "^1.0.0-rc.15", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.14.3" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^18.0.2", + "@angular/cli": "^18.0.2", + "@angular/compiler-cli": "^18.0.0", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.1.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.4.2" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1802.14", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.14.tgz", + "integrity": "sha512-eplaGCXSlPwf1f4XwyzsYTd8/lJ0/Adm6XsODsBxvkZlIpLcps80/h2lH5MVJpoDREzIFu1BweDpYCoNK5yYZg==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "18.2.14", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/build-angular": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.14.tgz", + "integrity": "sha512-ycie4OhvNv8eNVqvq46pCIf6kB50xbMOdnAVqmlj+BaQjWbGjUQPjAmp4VGqeDZZ/lW82xkfTmJZxc6pYp7YdQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1802.14", + "@angular-devkit/build-webpack": "0.1802.14", + "@angular-devkit/core": "18.2.14", + "@angular/build": "18.2.14", + "@babel/core": "7.25.2", + "@babel/generator": "7.25.0", + "@babel/helper-annotate-as-pure": "7.24.7", + "@babel/helper-split-export-declaration": "7.24.7", + "@babel/plugin-transform-async-generator-functions": "7.25.0", + "@babel/plugin-transform-async-to-generator": "7.24.7", + "@babel/plugin-transform-runtime": "7.24.7", + "@babel/preset-env": "7.25.3", + "@babel/runtime": "7.25.0", + "@discoveryjs/json-ext": "0.6.1", + "@ngtools/webpack": "18.2.14", + "@vitejs/plugin-basic-ssl": "1.1.0", + "ansi-colors": "4.1.3", + "autoprefixer": "10.4.20", + "babel-loader": "9.1.3", + "browserslist": "^4.21.5", + "copy-webpack-plugin": "12.0.2", + "critters": "0.0.24", + "css-loader": "7.1.2", + "esbuild-wasm": "0.23.0", + "fast-glob": "3.3.2", + "http-proxy-middleware": "3.0.3", + "https-proxy-agent": "7.0.5", + "istanbul-lib-instrument": "6.0.3", + "jsonc-parser": "3.3.1", + "karma-source-map-support": "1.4.0", + "less": "4.2.0", + "less-loader": "12.2.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.3.1", + "magic-string": "0.30.11", + "mini-css-extract-plugin": "2.9.0", + "mrmime": "2.0.0", + "open": "10.1.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "7.0.0", + "picomatch": "4.0.2", + "piscina": "4.6.1", + "postcss": "8.4.41", + "postcss-loader": "8.1.1", + "resolve-url-loader": "5.0.0", + "rxjs": "7.8.1", + "sass": "1.77.6", + "sass-loader": "16.0.0", + "semver": "7.6.3", + "source-map-loader": "5.0.0", + "source-map-support": "0.5.21", + "terser": "5.31.6", + "tree-kill": "1.2.2", + "tslib": "2.6.3", + "watchpack": "2.4.1", + "webpack": "5.94.0", + "webpack-dev-middleware": "7.4.2", + "webpack-dev-server": "5.0.4", + "webpack-merge": "6.0.1", + "webpack-subresource-integrity": "5.1.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.23.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", + "@web/test-runner": "^0.18.0", + "browser-sync": "^3.0.2", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", + "karma": "^6.3.0", + "ng-packagr": "^18.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=5.4 <5.6" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "@web/test-runner": { + "optional": true + }, + "browser-sync": { + "optional": true + }, + "jest": { + "optional": true + }, + "jest-environment-jsdom": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1802.14", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.14.tgz", + "integrity": "sha512-cccne0SG4BaQHsKRRZCi/wMLJ7yFXrwvE8w+Kug3HdpJJoyH3FeG386EQuca/azslQlK+c5g4ywSZdXeNkGazA==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1802.14", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^5.0.2" + } + }, + "node_modules/@angular-devkit/core": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.14.tgz", + "integrity": "sha512-UGIGOjXuOyCW+5S4tINu7e6LOu738CmTw3h7Ui1I8OzdTIYJcYJrei8sgrwDwOYADRal+p0MeMlnykH3TM5XBA==", + "dev": true, + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.14.tgz", + "integrity": "sha512-mukjZIHHB7gWratq8fZwUq5WZ+1bF4feG/idXr1wgQ+/FqWjs2PP7HDesHVcPymmRulpTyCpB7TNB1O1fgnCpA==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "18.2.14", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.11", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/animations": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.13.tgz", + "integrity": "sha512-rG5J5Ek5Hg+Tz2NjkNOaG6PupiNK/lPfophXpsR1t/nWujqnMWX2krahD/i6kgD+jNWNKCJCYSOVvCx/BHOtKA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "18.2.13" + } + }, + "node_modules/@angular/build": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.14.tgz", + "integrity": "sha512-9g24Oe/ZLULacW3hEpRCjSZIJPJTzN5BeFbA27epSV5NsrQOoeUGsEpRs90Zmt6eReO0fW1BGshWRoZtpSedcw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1802.14", + "@babel/core": "7.25.2", + "@babel/helper-annotate-as-pure": "7.24.7", + "@babel/helper-split-export-declaration": "7.24.7", + "@babel/plugin-syntax-import-attributes": "7.24.7", + "@inquirer/confirm": "3.1.22", + "@vitejs/plugin-basic-ssl": "1.1.0", + "browserslist": "^4.23.0", + "critters": "0.0.24", + "esbuild": "0.23.0", + "fast-glob": "3.3.2", + "https-proxy-agent": "7.0.5", + "listr2": "8.2.4", + "lmdb": "3.0.13", + "magic-string": "0.30.11", + "mrmime": "2.0.0", + "parse5-html-rewriting-stream": "7.0.0", + "picomatch": "4.0.2", + "piscina": "4.6.1", + "rollup": "4.22.4", + "sass": "1.77.6", + "semver": "7.6.3", + "vite": "5.4.14", + "watchpack": "2.4.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", + "less": "^4.2.0", + "postcss": "^8.4.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=5.4 <5.6" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "less": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular/cdk": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.14.tgz", + "integrity": "sha512-vDyOh1lwjfVk9OqoroZAP8pf3xxKUvyl+TVR8nJxL4c5fOfUFkD7l94HaanqKSRwJcI2xiztuu92IVoHn8T33Q==", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/cli": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.14.tgz", + "integrity": "sha512-kWgRRQtJPkr8iwN7DMbTi3sXOnv7H5QhbU/GgD3nNX3D8YCSPmnby4PAE/P3wn7FsIK9JsSchsCt7MZ37Urh9A==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1802.14", + "@angular-devkit/core": "18.2.14", + "@angular-devkit/schematics": "18.2.14", + "@inquirer/prompts": "5.3.8", + "@listr2/prompt-adapter-inquirer": "2.0.15", + "@schematics/angular": "18.2.14", + "@yarnpkg/lockfile": "1.1.0", + "ini": "4.1.3", + "jsonc-parser": "3.3.1", + "listr2": "8.2.4", + "npm-package-arg": "11.0.3", + "npm-pick-manifest": "9.1.0", + "pacote": "18.0.6", + "resolve": "1.22.8", + "semver": "7.6.3", + "symbol-observable": "4.0.0", + "yargs": "17.7.2" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/common": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.13.tgz", + "integrity": "sha512-4ZqrNp1PoZo7VNvW+sbSc2CB2axP1sCH2wXl8B0wdjsj8JY1hF1OhuugwhpAHtGxqewed2kCXayE+ZJqSTV4jw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "18.2.13", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.13.tgz", + "integrity": "sha512-TzWcrkopyjFF+WeDr2cRe8CcHjU72KfYV3Sm2TkBkcXrkYX5sDjGWrBGrG3hRB4e4okqchrOCvm1MiTdy2vKMA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "18.2.13" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + } + } + }, + "node_modules/@angular/compiler-cli": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.13.tgz", + "integrity": "sha512-DBSh4AQwkiJDSiVvJATRmjxf6wyUs9pwQLgaFdSlfuTRO+sdb0J2z1r3BYm8t0IqdoyXzdZq2YCH43EmyvD71g==", + "dev": true, + "dependencies": { + "@babel/core": "7.25.2", + "@jridgewell/sourcemap-codec": "^1.4.14", + "chokidar": "^4.0.0", + "convert-source-map": "^1.5.1", + "reflect-metadata": "^0.2.0", + "semver": "^7.0.0", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js", + "ngcc": "bundles/ngcc/index.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/compiler": "18.2.13", + "typescript": ">=5.4 <5.6" + } + }, + "node_modules/@angular/compiler-cli/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@angular/compiler-cli/node_modules/readdirp": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", + "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@angular/core": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.13.tgz", + "integrity": "sha512-8mbWHMgO95OuFV1Ejy4oKmbe9NOJ3WazQf/f7wks8Bck7pcihd0IKhlPBNjFllbF5o+04EYSwFhEtvEgjMDClA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.14.10" + } + }, + "node_modules/@angular/forms": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.13.tgz", + "integrity": "sha512-A67D867fu3DSBhdLWWZl/F5pr7v2+dRM2u3U7ZJ0ewh4a+sv+0yqWdJW+a8xIoiHxS+btGEJL2qAKJiH+MCFfg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "18.2.13", + "@angular/core": "18.2.13", + "@angular/platform-browser": "18.2.13", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/material": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.14.tgz", + "integrity": "sha512-28pxzJP49Mymt664WnCtPkKeg7kXUsQKTKGf/Kl95rNTEdTJLbnlcc8wV0rT0yQNR7kXgpfBnG7h0ETLv/iu5Q==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": "^18.0.0 || ^19.0.0", + "@angular/cdk": "18.2.14", + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", + "@angular/forms": "^18.0.0 || ^19.0.0", + "@angular/platform-browser": "^18.0.0 || ^19.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.13.tgz", + "integrity": "sha512-tu7ZzY6qD3ATdWFzcTcsAKe7M6cJeWbT/4/bF9unyGO3XBPcNYDKoiz10+7ap2PUd0fmPwvuvTvSNJiFEBnB8Q==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/animations": "18.2.13", + "@angular/common": "18.2.13", + "@angular/core": "18.2.13" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.13.tgz", + "integrity": "sha512-kbQCf9+8EpuJC7buBxhSiwBtXvjAwAKh6MznD6zd2pyCYqfY6gfRCZQRtK59IfgVtKmEONWI9grEyNIRoTmqJg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "18.2.13", + "@angular/compiler": "18.2.13", + "@angular/core": "18.2.13", + "@angular/platform-browser": "18.2.13" + } + }, + "node_modules/@angular/router": { + "version": "18.2.13", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.13.tgz", + "integrity": "sha512-VKmfgi/r/CkyBq9nChQ/ptmfu0JT/8ONnLVJ5H+SkFLRYJcIRyHLKjRihMCyVm6xM5yktOdCaW73NTQrFz7+bg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "18.2.13", + "@angular/core": "18.2.13", + "@angular/platform-browser": "18.2.13", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", + "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz", + "integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", + "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", + "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.26.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", + "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz", + "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-remap-async-to-generator": "^7.25.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/traverse": "^7.25.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", + "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-remap-async-to-generator": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", + "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", + "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.2", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.24.7", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.0", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoped-functions": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.0", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", + "@babel/plugin-transform-dotall-regex": "^7.24.7", + "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", + "@babel/plugin-transform-dynamic-import": "^7.24.7", + "@babel/plugin-transform-exponentiation-operator": "^7.24.7", + "@babel/plugin-transform-export-namespace-from": "^7.24.7", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", + "@babel/plugin-transform-json-strings": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", + "@babel/plugin-transform-member-expression-literals": "^7.24.7", + "@babel/plugin-transform-modules-amd": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", + "@babel/plugin-transform-modules-umd": "^7.24.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-new-target": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.7", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-object-super": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-property-literals": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-reserved-words": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.7", + "@babel/plugin-transform-spread": "^7.24.7", + "@babel/plugin-transform-sticky-regex": "^7.24.7", + "@babel/plugin-transform-template-literals": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.8", + "@babel/plugin-transform-unicode-escapes": "^7.24.7", + "@babel/plugin-transform-unicode-property-regex": "^7.24.7", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.37.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", + "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/types": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.1.tgz", + "integrity": "sha512-boghen8F0Q8D+0/Q1/1r6DUEieUJ8w2a1gIknExMSHBsJFOr2+0KUfHiVYBvucPwl3+RU5PFBK833FjFCh3BhA==", + "dev": true, + "engines": { + "node": ">=14.17.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", + "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", + "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", + "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", + "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", + "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", + "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", + "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", + "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", + "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", + "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", + "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", + "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", + "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", + "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", + "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", + "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", + "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", + "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", + "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", + "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", + "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", + "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", + "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", + "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@genkit-ai/ai": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.15.tgz", + "integrity": "sha512-cCB0Lp1Q7umV3fAKvfydz99CEeSq8n5KBfp+3oAIwFWgRvRTBgw0Y5tLtuis4TptCD2ZqqnoiedPW+3JXQU+VQ==", + "dependencies": { + "@genkit-ai/core": "1.0.0-rc.15", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.11.19", + "colorette": "^2.0.20", + "dotprompt": "^1.0.0-dev.3 || ^1", + "json5": "^2.2.3", + "node-fetch": "^3.3.2", + "partial-json": "^0.1.7", + "uuid": "^10.0.0" + } + }, + "node_modules/@genkit-ai/ai/node_modules/@types/node": { + "version": "20.17.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.17.tgz", + "integrity": "sha512-/WndGO4kIfMicEQLTi/mDANUu/iVUhT7KboZPdEqqHQ4aTS+3qT3U5gIqWDFV+XouorjfgGqvKILJeHhuQgFYg==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@genkit-ai/ai/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/@genkit-ai/core": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.15.tgz", + "integrity": "sha512-eq6nRTPELk7/wkhVXdaAgV1sVrLFgzcdiSeezTpsyhISH9gVWzcSHsNPwxdAR3K+szgo5PkomZQT6JIQKrlxsQ==", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "@types/json-schema": "^7.0.15", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "async-mutex": "^0.5.0", + "body-parser": "^1.20.3", + "cors": "^2.8.5", + "dotprompt": "^1.0.0-dev.3 || ^1", + "express": "^4.21.0", + "get-port": "^5.1.0", + "json-schema": "^0.4.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz", + "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.5.0.tgz", + "integrity": "sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/confirm": { + "version": "3.1.22", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.22.tgz", + "integrity": "sha512-gsAKIOWBm2Q87CDfs9fEo7wJT3fwWIJfnDGMn9Qy74gBnNFOACDNfhUzovubbJjWnKLGBln7/NcSmZwj5DuEXg==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", + "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "dev": true, + "dependencies": { + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.5", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/@inquirer/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", + "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", + "dev": true, + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.2.0.tgz", + "integrity": "sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/expand": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.3.0.tgz", + "integrity": "sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.10.tgz", + "integrity": "sha512-Ey6176gZmeqZuY/W/nZiUyvmb1/qInjcpiZjXWi6nON+nxJpD1bxtSoBxNliGISae32n6OwbY+TSXPZ1CfS4bw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.3.0.tgz", + "integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-1.1.0.tgz", + "integrity": "sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/password": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.2.0.tgz", + "integrity": "sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/prompts": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.3.8.tgz", + "integrity": "sha512-b2BudQY/Si4Y2a0PdZZL6BeJtl8llgeZa7U2j47aaJSCeAl1e4UI7y8a9bSkO3o/ZbZrgT5muy/34JbsjfIWxA==", + "dev": true, + "dependencies": { + "@inquirer/checkbox": "^2.4.7", + "@inquirer/confirm": "^3.1.22", + "@inquirer/editor": "^2.1.22", + "@inquirer/expand": "^2.1.22", + "@inquirer/input": "^2.2.9", + "@inquirer/number": "^1.0.10", + "@inquirer/password": "^2.1.22", + "@inquirer/rawlist": "^2.2.4", + "@inquirer/search": "^1.0.7", + "@inquirer/select": "^2.4.7" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/rawlist": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.3.0.tgz", + "integrity": "sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/search": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-1.1.0.tgz", + "integrity": "sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/select": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.5.0.tgz", + "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", + "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", + "dev": true, + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.1.tgz", + "integrity": "sha512-osjeBqMJ2lb/j/M8NCPjs1ylqWIcTRTycIhVB5pt6LgzgeRSb0YRZ7j9RfA8wIUrsr/medIuhVyonXRZWLyfdw==", + "dev": true, + "dependencies": { + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.1.2", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz", + "integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true + }, + "node_modules/@listr2/prompt-adapter-inquirer": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.15.tgz", + "integrity": "sha512-MZrGem/Ujjd4cPTLYDfCZK2iKKeiO/8OX13S6jqxldLs0Prf2aGqVlJ77nMBqMv7fzqgXEgjrNHLXcKR8l9lOg==", + "dev": true, + "dependencies": { + "@inquirer/type": "^1.5.1" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@inquirer/prompts": ">= 3 < 6" + } + }, + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.0.13.tgz", + "integrity": "sha512-uiKPB0Fv6WEEOZjruu9a6wnW/8jrjzlZbxXscMB8kuCJ1k6kHpcBnuvaAWcqhbI7rqX5GKziwWEdD+wi2gNLfA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.0.13.tgz", + "integrity": "sha512-bEVIIfK5mSQoG1R19qA+fJOvCB+0wVGGnXHT3smchBVahYBdlPn2OsZZKzlHWfb1E+PhLBmYfqB5zQXFP7hJig==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.0.13.tgz", + "integrity": "sha512-Yml1KlMzOnXj/tnW7yX8U78iAzTk39aILYvCPbqeewAq1kSzl+w59k/fiVkTBfvDi/oW/5YRxL+Fq+Y1Fr1r2Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.0.13.tgz", + "integrity": "sha512-afbVrsMgZ9dUTNUchFpj5VkmJRxvht/u335jUJ7o23YTbNbnpmXif3VKQGCtnjSh+CZaqm6N3CPG8KO3zwyZ1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.0.13.tgz", + "integrity": "sha512-vOtxu0xC0SLdQ2WRXg8Qgd8T32ak4SPqk5zjItRszrJk2BdeXqfGxBJbP7o4aOvSPSmSSv46Lr1EP4HXU8v7Kg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.0.13.tgz", + "integrity": "sha512-UCrMJQY/gJnOl3XgbWRZZUvGGBuKy6i0YNSptgMzHBjs+QYDYR1Mt/RLTOPy4fzzves65O1EDmlL//OzEqoLlA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@ngtools/webpack": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.14.tgz", + "integrity": "sha512-rT+Y4WR8QTVsijtb+YRqHcPTpd1ZiwRbklQXRTxU0YGFHpxpi+bhjmY8FjpPoAtdPO1Lg3l3KIZPZa0thG0FNg==", + "dev": true, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "typescript": ">=5.4 <5.6", + "webpack": "^5.54.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/agent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", + "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/@npmcli/fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz", + "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^4.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", + "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", + "dev": true, + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", + "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.1.tgz", + "integrity": "sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ==", + "dev": true, + "dependencies": { + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^4.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/package-json/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", + "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", + "dev": true, + "dependencies": { + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", + "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", + "dev": true, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", + "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "proc-log": "^4.0.0", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz", + "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz", + "integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", + "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz", + "integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz", + "integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz", + "integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", + "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", + "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz", + "integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", + "@opentelemetry/exporter-zipkin": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/sdk-trace-node": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", + "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.25.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/propagator-b3": "1.25.1", + "@opentelemetry/propagator-jaeger": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", + "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", + "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", + "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", + "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", + "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", + "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", + "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", + "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", + "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", + "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", + "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", + "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", + "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", + "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", + "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", + "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@schematics/angular": { + "version": "18.2.14", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.14.tgz", + "integrity": "sha512-CHh6ew2Az71UlvVcnYeuMEwjwkZqR7y/9ebLzFRvczC71ZL8qPVBpBTVGbCpGBd54VEbCZVWRxBQoZZ5LP/aBw==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "18.2.14", + "@angular-devkit/schematics": "18.2.14", + "jsonc-parser": "3.3.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@sigstore/bundle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", + "integrity": "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==", + "dev": true, + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.1.0.tgz", + "integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==", + "dev": true, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.3.tgz", + "integrity": "sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ==", + "dev": true, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.3.2.tgz", + "integrity": "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==", + "dev": true, + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", + "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", + "dev": true, + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.2.1.tgz", + "integrity": "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==", + "dev": true, + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true + }, + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "dev": true, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.1.tgz", + "integrity": "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==", + "dev": true, + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/http-proxy": { + "version": "1.17.15", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jasmine": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.5.tgz", + "integrity": "sha512-SaCZ3kM5NjOiJqMRYwHpLbTfUC2Dyk1KS3QanNFsUYPGTk70CWVK/J9ueun6zNhw/UkgV7xl8V4ZLQZNRbfnNw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==" + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true + }, + "node_modules/@types/ws": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", + "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vitejs/plugin-basic-ssl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", + "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", + "dev": true, + "engines": { + "node": ">=14.6.0" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "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==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/babel-loader": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", + "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", + "dev": true, + "dependencies": { + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.12", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz", + "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.3", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz", + "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.3" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/bonjour-service": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", + "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "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==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "18.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", + "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001697", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001697.tgz", + "integrity": "sha512-GwNPlWJin8E+d7Gxq96jxM6w0w+VFeyyXRsjU58emtkYqnbwHqXm5uT2uCmO0RQE9htWknOP4xtBlLmM/gWxvQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/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==" + }, + "node_modules/cliui/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-deep/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.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==", + "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==" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz", + "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.0.2", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/connect/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", + "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", + "dev": true, + "dependencies": { + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.1", + "globby": "^14.0.0", + "normalize-path": "^3.0.0", + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/core-js-compat": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz", + "integrity": "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.24.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig/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==", + "dev": true + }, + "node_modules/cosmiconfig/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/critters": { + "version": "0.0.24", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.24.tgz", + "integrity": "sha512-Oyqew0FGM0wYUSNqR0L6AteO5MpMoUU0rhKRieXeiKs+PmRTxiJMyaunYB2KF6fQ3dzChXKCpbFOEJx3OQ1v/Q==", + "deprecated": "Ownership of Critters has moved to the Nuxt team, who will be maintaining the project going forward. If you'd like to keep using Critters, please switch to the actively-maintained fork at https://github.com/danielroe/beasties", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^5.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.2", + "htmlparser2": "^8.0.2", + "postcss": "^8.4.23", + "postcss-media-query-parser": "^0.2.3" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-loader": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "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-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "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==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "dev": true + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "dev": true, + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "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==", + "dev": true, + "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/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "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==", + "dev": true, + "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/dotprompt": { + "version": "1.0.0-dev.3", + "resolved": "https://registry.npmjs.org/dotprompt/-/dotprompt-1.0.0-dev.3.tgz", + "integrity": "sha512-gAcIhG+vzZlrZcBN/lO8gEMUtXHKMLbs1+snJuZrPkZnNgnWrzz1hYN4vYotpng/5fHsYSPUEckECy6qUNSrsQ==", + "dependencies": { + "handlebars": "^4.7.8", + "yaml": "^2.5.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.91", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.91.tgz", + "integrity": "sha512-sNSHHyq048PFmZY4S90ax61q+gLCs0X0YmcOII9wG9S2XwbVr+h4VW2wWhnbp/Eys3cCwTxVF292W3qPaxIapQ==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "dev": true, + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", + "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.2.tgz", + "integrity": "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "punycode": "^1.4.1", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "devOptional": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", + "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.0", + "@esbuild/android-arm": "0.23.0", + "@esbuild/android-arm64": "0.23.0", + "@esbuild/android-x64": "0.23.0", + "@esbuild/darwin-arm64": "0.23.0", + "@esbuild/darwin-x64": "0.23.0", + "@esbuild/freebsd-arm64": "0.23.0", + "@esbuild/freebsd-x64": "0.23.0", + "@esbuild/linux-arm": "0.23.0", + "@esbuild/linux-arm64": "0.23.0", + "@esbuild/linux-ia32": "0.23.0", + "@esbuild/linux-loong64": "0.23.0", + "@esbuild/linux-mips64el": "0.23.0", + "@esbuild/linux-ppc64": "0.23.0", + "@esbuild/linux-riscv64": "0.23.0", + "@esbuild/linux-s390x": "0.23.0", + "@esbuild/linux-x64": "0.23.0", + "@esbuild/netbsd-x64": "0.23.0", + "@esbuild/openbsd-arm64": "0.23.0", + "@esbuild/openbsd-x64": "0.23.0", + "@esbuild/sunos-x64": "0.23.0", + "@esbuild/win32-arm64": "0.23.0", + "@esbuild/win32-ia32": "0.23.0", + "@esbuild/win32-x64": "0.23.0" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.23.0.tgz", + "integrity": "sha512-6jP8UmWy6R6TUUV8bMuC3ZyZ6lZKI56x0tkxyCIqWwRRJ/DgeQKneh/Oid5EoGoPFLrGNkz47ZEtWAYuiY/u9g==", + "dev": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "dev": true + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fastq": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", + "dev": true, + "dependencies": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dev": true, + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/genkit": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.15.tgz", + "integrity": "sha512-2dFUQ4qnXxv98ijs8/dr3yjof7Fev+FT+alxuRwXxUhpqGc4ei/0p1xgtFeq5j6sWBoRcUHNvzCPrMwgDDd4pA==", + "dependencies": { + "@genkit-ai/ai": "1.0.0-rc.15", + "@genkit-ai/core": "1.0.0-rc.15", + "uuid": "^10.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", + "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz", + "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-middleware": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.3.tgz", + "integrity": "sha512-usY0HG5nyDUwtqpiZdETNbmKtw3QQ1jwYFZ9wi5iHzX2BcILwQKtYDJPo7XHTsu5Z0B2Hj3W9NNnbd+AjFWjqg==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.15", + "debug": "^4.3.6", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.3", + "is-plain-object": "^5.0.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "engines": { + "node": ">=10.18" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", + "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", + "dev": true, + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-in-the-middle": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.12.0.tgz", + "integrity": "sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "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-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "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-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true + }, + "node_modules/is-network-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", + "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jasmine-core": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.2.tgz", + "integrity": "sha512-2oIUMGn00FdUiqz6epiiJr7xcFyNYj3rDcfmnzfkBnHyBQ3cBQUs4mmyGsOb7TTLb9kxk7dBcmEmqhDKkBoDyA==", + "dev": true + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/karma": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", + "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", + "dev": true, + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.7.2", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-chrome-launcher": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz", + "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==", + "dev": true, + "dependencies": { + "which": "^1.2.1" + } + }, + "node_modules/karma-coverage": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.1.tgz", + "integrity": "sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.0.5", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/karma-coverage/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma-coverage/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/karma-jasmine": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", + "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", + "dev": true, + "dependencies": { + "jasmine-core": "^4.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "karma": "^6.0.0" + } + }, + "node_modules/karma-jasmine-html-reporter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz", + "integrity": "sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==", + "dev": true, + "peerDependencies": { + "jasmine-core": "^4.0.0 || ^5.0.0", + "karma": "^6.0.0", + "karma-jasmine": "^5.0.0" + } + }, + "node_modules/karma-jasmine/node_modules/jasmine-core": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.6.1.tgz", + "integrity": "sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==", + "dev": true + }, + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "dependencies": { + "source-map-support": "^0.5.5" + } + }, + "node_modules/karma/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/karma/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==", + "dev": true + }, + "node_modules/karma/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/karma/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/karma/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/launch-editor": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "node_modules/less": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", + "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.2.0.tgz", + "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==", + "dev": true, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/listr2": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", + "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", + "dev": true, + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "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/lmdb": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.0.13.tgz", + "integrity": "sha512-UGe+BbaSUQtAMZobTb4nHvFMrmvuAQKSeaqAX2meTEQjfsbpl5sxdHD8T72OnwD4GU9uwNhYXIVe4QGs8N9Zyw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "msgpackr": "^1.10.2", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.2.2", + "ordered-binary": "^1.4.1", + "weak-lru-cache": "^1.2.2" + }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "3.0.13", + "@lmdb/lmdb-darwin-x64": "3.0.13", + "@lmdb/lmdb-linux-arm": "3.0.13", + "@lmdb/lmdb-linux-arm64": "3.0.13", + "@lmdb/lmdb-linux-x64": "3.0.13", + "@lmdb/lmdb-win32-x64": "3.0.13" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "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/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "dev": true, + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/long": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-fetch-happen": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", + "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", + "dev": true, + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.0.tgz", + "integrity": "sha512-4eirfZ7thblFmqFjywlTmuWVSvccHAJbn1r8qQLzmTO11qcqpohOjmY2mFce6x7x7WtskzRqApPD0hv+Oa74jg==", + "dev": true, + "dependencies": { + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.3.0", + "tree-dump": "^1.0.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">= 4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", + "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "dev": true, + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", + "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", + "dev": true, + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "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==" + }, + "node_modules/msgpackr": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.2.tgz", + "integrity": "sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==", + "dev": true, + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "!win32" + ], + "dependencies": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node_modules/nice-napi/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.3.1.tgz", + "integrity": "sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", + "semver": "^7.3.5", + "tar": "^6.2.1", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", + "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-install-checks": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", + "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", + "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", + "dev": true, + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-packlist": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", + "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", + "dev": true, + "dependencies": { + "ignore-walk": "^6.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz", + "integrity": "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==", + "dev": true, + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", + "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", + "dev": true, + "dependencies": { + "@npmcli/redact": "^2.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "dev": true, + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/ordered-binary": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.3.tgz", + "integrity": "sha512-oGFr3T+pYdTGJ+YFEILMpS3es+GiIbs9h/XQrclBXUtd44ey7XwfsMzM31f64I1SQOawDoDr/D823kNCADI8TA==", + "dev": true + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", + "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/pacote": { + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-18.0.6.tgz", + "integrity": "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==", + "dev": true, + "dependencies": { + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/package-json": "^5.1.0", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^8.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "devOptional": true, + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-7.0.0.tgz", + "integrity": "sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==", + "dev": true, + "dependencies": { + "entities": "^4.3.0", + "parse5": "^7.0.0", + "parse5-sax-parser": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", + "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==", + "dev": true, + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==" + }, + "node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/piscina": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.6.1.tgz", + "integrity": "sha512-z30AwWGtQE+Apr+2WBZensP2lIvwoaMcOPkQlIEmSGMJNUvaYACylPYrQM6wSdUNJlnDVMSpLv7xTMJqlVshOA==", + "dev": true, + "optionalDependencies": { + "nice-napi": "^1.0.2" + } + }, + "node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "dev": true, + "dependencies": { + "find-up": "^6.3.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "dev": true, + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-loader": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", + "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", + "dev": true, + "dependencies": { + "cosmiconfig": "^9.0.0", + "jiti": "^1.20.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/proc-log": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true + }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true, + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", + "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", + "dev": true + }, + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "dev": true, + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.0.tgz", + "integrity": "sha512-/Tvpny/RVVicqlYTKwt/GtpZRsPG1CmJNhxVKGz+Sy/4MONfXCVNK69MFgGKdUt0/324q3ClI2dICcPgISrC8g==", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", + "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.22.4", + "@rollup/rollup-android-arm64": "4.22.4", + "@rollup/rollup-darwin-arm64": "4.22.4", + "@rollup/rollup-darwin-x64": "4.22.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", + "@rollup/rollup-linux-arm-musleabihf": "4.22.4", + "@rollup/rollup-linux-arm64-gnu": "4.22.4", + "@rollup/rollup-linux-arm64-musl": "4.22.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", + "@rollup/rollup-linux-riscv64-gnu": "4.22.4", + "@rollup/rollup-linux-s390x-gnu": "4.22.4", + "@rollup/rollup-linux-x64-gnu": "4.22.4", + "@rollup/rollup-linux-x64-musl": "4.22.4", + "@rollup/rollup-win32-arm64-msvc": "4.22.4", + "@rollup/rollup-win32-ia32-msvc": "4.22.4", + "@rollup/rollup-win32-x64-msvc": "4.22.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sass": { + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-loader": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.0.tgz", + "integrity": "sha512-n13Z+3rU9A177dk4888czcVFiC8CL9dii4qpXWUg3YIIgZEvi9TCFKjOQcbK0kJM7DJu9VucrZFddvNfYCPwtw==", + "dev": true, + "dependencies": { + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true, + "optional": true + }, + "node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sigstore": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.3.1.tgz", + "integrity": "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==", + "dev": true, + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dev": true, + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", + "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", + "dev": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/ssri": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", + "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "dev": true, + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dev": true, + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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==", + "dev": true, + "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/string-width-cjs": { + "name": "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/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==", + "dev": true + }, + "node_modules/string-width-cjs/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/terser": { + "version": "5.31.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", + "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", + "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/thingies": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", + "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", + "dev": true, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "tslib": "^2" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tree-dump": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz", + "integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==", + "dev": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "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==" + }, + "node_modules/tuf-js": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", + "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", + "dev": true, + "dependencies": { + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.40.tgz", + "integrity": "sha512-us1E3K+3jJppDBa3Tl0L3MOJiGhe1C6P0+nIvQAFYbxlMAx0h81eOwLmU57xgqToduDDPx3y5QsdjPfDu+FgOQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.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", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vite/node_modules/postcss": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "dev": true, + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webpack": { + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", + "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^4.6.0", + "mime-types": "^2.1.31", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz", + "integrity": "sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.4.0", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "rimraf": "^5.0.5", + "schema-utils": "^4.2.0", + "selfsigned": "^2.4.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^7.1.0", + "ws": "^8.16.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-dev-server/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webpack-dev-server/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "dependencies": { + "typed-assert": "^1.0.8" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", + "webpack": "^5.12.0" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/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==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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==", + "dev": true + }, + "node_modules/wrap-ansi/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/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==" + }, + "node_modules/yargs/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", + "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", + "peerDependencies": { + "zod": "^3.24.1" + } + }, + "node_modules/zone.js": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz", + "integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==" + } + } +} diff --git a/samples/js-angular/genkit-app/package.json b/samples/js-angular/genkit-app/package.json index 89cd74b7f..8132bf401 100644 --- a/samples/js-angular/genkit-app/package.json +++ b/samples/js-angular/genkit-app/package.json @@ -23,7 +23,7 @@ "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3", - "genkit": "^0.9.0-rc || ^0.9" + "genkit": "^1.0.0-rc.15" }, "devDependencies": { "@angular-devkit/build-angular": "^18.0.2", diff --git a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts index 1fa9d6900..29cf2b40a 100644 --- a/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts +++ b/samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts @@ -32,7 +32,7 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { streamFlow } from 'genkit/client'; +import { streamFlow } from 'genkit/beta/client'; const url = 'http://127.0.0.1:3400/chatbotFlow'; diff --git a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts index c9444ec95..4de0d4ebb 100644 --- a/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts +++ b/samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts @@ -18,7 +18,7 @@ import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; -import { streamFlow } from 'genkit/client'; +import { streamFlow } from 'genkit/beta/client'; const url = 'http://127.0.0.1:3400/streamCharacters'; diff --git a/samples/js-angular/server/package-lock.json b/samples/js-angular/server/package-lock.json new file mode 100644 index 000000000..e1a55f67d --- /dev/null +++ b/samples/js-angular/server/package-lock.json @@ -0,0 +1,6374 @@ +{ + "name": "js-angular", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "js-angular", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@genkit-ai/express": "^1.0.0-rc.15", + "@genkit-ai/vertexai": "^1.0.0-rc.15", + "express": "^4.21.0", + "genkit": "^1.0.0-rc.15", + "partial-json": "^0.1.7" + }, + "devDependencies": { + "genkit-cli": "^1.0.0-rc.15", + "tsx": "^4.19.2", + "typescript": "^5.4.5" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.24.3", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.24.3.tgz", + "integrity": "sha512-916wJXO6T6k8R6BAAcLhLPv/pnLGy7YSEBZXZ1XTFbLcTZE8oTy3oDW9WJf9KKZwMvVcePIfoTSvzXHRcGxkQQ==", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7", + "web-streams-polyfill": "^3.2.1" + } + }, + "node_modules/@anthropic-ai/sdk/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@anthropic-ai/vertex-sdk": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@anthropic-ai/vertex-sdk/-/vertex-sdk-0.4.3.tgz", + "integrity": "sha512-2Uef0C5P2Hx+T88RnUSRA3u4aZqmqnrRSOb2N64ozgKPiSUPTM5JlggAq2b32yWMj5d3MLYa6spJXKMmHXOcoA==", + "dependencies": { + "@anthropic-ai/sdk": ">=0.14 <1", + "google-auth-library": "^9.4.2" + } + }, + "node_modules/@asteasolutions/zod-to-openapi": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.3.0.tgz", + "integrity": "sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==", + "dev": true, + "dependencies": { + "openapi3-ts": "^4.1.2" + }, + "peerDependencies": { + "zod": "^3.20.2" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@fastify/busboy": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", + "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==", + "optional": true + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.3.tgz", + "integrity": "sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==", + "optional": true + }, + "node_modules/@firebase/app-types": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", + "optional": true + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.4.tgz", + "integrity": "sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==", + "optional": true + }, + "node_modules/@firebase/component": { + "version": "0.6.12", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.12.tgz", + "integrity": "sha512-YnxqjtohLbnb7raXt2YuA44cC1wA9GiehM/cmxrsoxKlFxBLy2V0OkRSj9gpngAE0UoJ421Wlav9ycO7lTPAUw==", + "optional": true, + "dependencies": { + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.11.tgz", + "integrity": "sha512-gLrw/XeioswWUXgpVKCPAzzoOuvYNqK5fRUeiJTzO7Mlp9P6ylFEyPJlRBl1djqYye641r3MX6AmIeMXwjgwuQ==", + "optional": true, + "dependencies": { + "@firebase/app-check-interop-types": "0.3.3", + "@firebase/auth-interop-types": "0.2.4", + "@firebase/component": "0.6.12", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.2.tgz", + "integrity": "sha512-5zvdnMsfDHvrQAVM6jBS7CkBpu+z3YbpFdhxRsrK1FP45IEfxlzpeuEUb17D/tpM10vfq4Ok0x5akIBaCv7gfA==", + "optional": true, + "dependencies": { + "@firebase/component": "0.6.12", + "@firebase/database": "1.0.11", + "@firebase/database-types": "1.0.8", + "@firebase/logger": "0.4.4", + "@firebase/util": "1.10.3", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.8.tgz", + "integrity": "sha512-6lPWIGeufhUq1heofZULyVvWFhD01TUrkkB9vyhmksjZ4XF7NaivQp9rICMk7QNhqwa+uDCaj4j+Q8qqcSVZ9g==", + "optional": true, + "dependencies": { + "@firebase/app-types": "0.9.3", + "@firebase/util": "1.10.3" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", + "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "optional": true, + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@firebase/util": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.3.tgz", + "integrity": "sha512-wfoF5LTy0m2ufUapV0ZnpcGQvuavTbJ5Qr1Ze9OJGL70cSMvhDyjS4w2121XdA3lGZSTOsDOyGhpoDtYwck85A==", + "optional": true, + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@genkit-ai/ai": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.15.tgz", + "integrity": "sha512-cCB0Lp1Q7umV3fAKvfydz99CEeSq8n5KBfp+3oAIwFWgRvRTBgw0Y5tLtuis4TptCD2ZqqnoiedPW+3JXQU+VQ==", + "dependencies": { + "@genkit-ai/core": "1.0.0-rc.15", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.11.19", + "colorette": "^2.0.20", + "dotprompt": "^1.0.0-dev.3 || ^1", + "json5": "^2.2.3", + "node-fetch": "^3.3.2", + "partial-json": "^0.1.7", + "uuid": "^10.0.0" + } + }, + "node_modules/@genkit-ai/ai/node_modules/@types/node": { + "version": "20.17.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.17.tgz", + "integrity": "sha512-/WndGO4kIfMicEQLTi/mDANUu/iVUhT7KboZPdEqqHQ4aTS+3qT3U5gIqWDFV+XouorjfgGqvKILJeHhuQgFYg==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@genkit-ai/ai/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/@genkit-ai/ai/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@genkit-ai/core": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.15.tgz", + "integrity": "sha512-eq6nRTPELk7/wkhVXdaAgV1sVrLFgzcdiSeezTpsyhISH9gVWzcSHsNPwxdAR3K+szgo5PkomZQT6JIQKrlxsQ==", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "@types/json-schema": "^7.0.15", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "async-mutex": "^0.5.0", + "body-parser": "^1.20.3", + "cors": "^2.8.5", + "dotprompt": "^1.0.0-dev.3 || ^1", + "express": "^4.21.0", + "get-port": "^5.1.0", + "json-schema": "^0.4.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/express": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/express/-/express-1.0.0-rc.15.tgz", + "integrity": "sha512-O5O5i1dVetBIu5cFQJhOjp+Xjt65hXzzJaZUGNQ3InOKB8obNltetQGPFNn+euTVfPsMXOZbZhTIXAXprwu70A==", + "dependencies": { + "body-parser": "^1.20.3", + "cors": "^2.8.5" + }, + "peerDependencies": { + "express": "^4.21.1", + "genkit": "^1.0.0-rc.15" + } + }, + "node_modules/@genkit-ai/telemetry-server": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.15.tgz", + "integrity": "sha512-pJhT1JUYBPsmHfRjrJ0WIzh1n78haDDVlyEZmGdM3Tr338yXxQB/mJhKI7sUEpu2O/uerxOI1kzNxPobTPjmjQ==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@genkit-ai/tools-common": "1.0.0-rc.15", + "@google-cloud/firestore": "^7.6.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/context-async-hooks": "^1.25.0", + "@opentelemetry/core": "^1.25.0", + "@opentelemetry/sdk-metrics": "^1.25.0", + "@opentelemetry/sdk-node": "^0.52.0", + "@opentelemetry/sdk-trace-base": "^1.25.0", + "async-mutex": "^0.5.0", + "express": "^4.21.0", + "zod": "^3.22.4" + } + }, + "node_modules/@genkit-ai/tools-common": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.15.tgz", + "integrity": "sha512-/kYSlspK/3Z1BbFMXjkqCYW85HVBVYnupJeRZYSIs2kEer/GJU1epiSXBo8K70w1TrC3GK9t2CSh2cILW9PvLg==", + "dev": true, + "dependencies": { + "@asteasolutions/zod-to-openapi": "^7.0.0", + "@trpc/server": "10.45.0", + "adm-zip": "^0.5.12", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "axios": "^1.7.7", + "body-parser": "^1.20.2", + "chokidar": "^3.5.3", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "configstore": "^5.0.1", + "express": "^4.21.0", + "get-port": "5.1.1", + "glob": "^10.3.12", + "inquirer": "^8.2.0", + "js-yaml": "^4.1.0", + "json-2-csv": "^5.5.1", + "json-schema": "^0.4.0", + "terminate": "^2.6.1", + "tsx": "^4.19.2", + "uuid": "^9.0.1", + "winston": "^3.11.0", + "yaml": "^2.4.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.4" + } + }, + "node_modules/@genkit-ai/vertexai": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@genkit-ai/vertexai/-/vertexai-1.0.0-rc.15.tgz", + "integrity": "sha512-HAGZYkKsmGuJqi3qda41hEwE25xlyz8J9kZaaevN4/RdmNWgLSE+YqUkfX/e6vPjq25/dp6a7d38UrpkfjXlgA==", + "dependencies": { + "@anthropic-ai/sdk": "^0.24.3", + "@anthropic-ai/vertex-sdk": "^0.4.0", + "@google-cloud/aiplatform": "^3.23.0", + "@google-cloud/vertexai": "^1.9.3", + "@mistralai/mistralai-gcp": "^1.3.5", + "google-auth-library": "^9.14.2", + "googleapis": "^140.0.1", + "node-fetch": "^3.3.2", + "openai": "^4.52.7" + }, + "optionalDependencies": { + "@google-cloud/bigquery": "^7.8.0", + "firebase-admin": ">=12.2" + }, + "peerDependencies": { + "genkit": "^1.0.0-rc.15" + } + }, + "node_modules/@google-cloud/aiplatform": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/@google-cloud/aiplatform/-/aiplatform-3.34.0.tgz", + "integrity": "sha512-Ii1CXJ59g5hcVYNZOx08XBV5nq0JIOSo2I9uC/WYkdXWekc3XSV9emRz8pKOQSULzrTOTnD80N4re49S07xfyQ==", + "dependencies": { + "google-gax": "^4.0.3", + "protobuf.js": "^1.1.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/bigquery": { + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@google-cloud/bigquery/-/bigquery-7.9.1.tgz", + "integrity": "sha512-ZkcRMpBoFLxIh6TiQBywA22yT3c2j0f07AHWEMjtYqMQzZQbFrpxuJU2COp3tyjZ91ZIGHe4gY7/dGZL88cltg==", + "optional": true, + "dependencies": { + "@google-cloud/common": "^5.0.0", + "@google-cloud/paginator": "^5.0.2", + "@google-cloud/precise-date": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "arrify": "^2.0.1", + "big.js": "^6.0.0", + "duplexify": "^4.0.0", + "extend": "^3.0.2", + "is": "^3.3.0", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/common": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-5.0.2.tgz", + "integrity": "sha512-V7bmBKYQyu0eVG2BFejuUjlBt+zrya6vtsKdY+JxMM/dNntPF41vZ9+LhOshEUH01zOHEqBSvI7Dad7ZS6aUeA==", + "optional": true, + "dependencies": { + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "arrify": "^2.0.1", + "duplexify": "^4.1.1", + "extend": "^3.0.2", + "google-auth-library": "^9.0.0", + "html-entities": "^2.5.2", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/firestore": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.0.tgz", + "integrity": "sha512-88uZ+jLsp1aVMj7gh3EKYH1aulTAMFAp8sH/v5a9w8q8iqSG27RiWLoxSAFr/XocZ9hGiWH1kEnBw+zl3xAgNA==", + "devOptional": true, + "dependencies": { + "@opentelemetry/api": "^1.3.0", + "fast-deep-equal": "^3.1.1", + "functional-red-black-tree": "^1.0.1", + "google-gax": "^4.3.3", + "protobufjs": "^7.2.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/paginator": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", + "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==", + "optional": true, + "dependencies": { + "arrify": "^2.0.0", + "extend": "^3.0.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/precise-date": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-4.0.0.tgz", + "integrity": "sha512-1TUx3KdaU3cN7nfCdNf+UVqA/PSX29Cjcox3fZZBtINlRrXVTmUkQnCKv2MbBUbCopbK4olAT1IHl76uZyCiVA==", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/projectify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", + "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@google-cloud/promisify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz", + "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google-cloud/storage": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.15.0.tgz", + "integrity": "sha512-/j/+8DFuEOo33fbdX0V5wjooOoFahEaMEdImHBmM2tH9MPHJYNtmXOf2sGUmZmiufSukmBEvdlzYgDkkgeBiVQ==", + "optional": true, + "dependencies": { + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "abort-controller": "^3.0.0", + "async-retry": "^1.3.3", + "duplexify": "^4.1.3", + "fast-xml-parser": "^4.4.1", + "gaxios": "^6.0.2", + "google-auth-library": "^9.6.3", + "html-entities": "^2.5.2", + "mime": "^3.0.0", + "p-limit": "^3.0.1", + "retry-request": "^7.0.0", + "teeny-request": "^9.0.0", + "uuid": "^8.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@google-cloud/storage/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@google-cloud/vertexai": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@google-cloud/vertexai/-/vertexai-1.9.3.tgz", + "integrity": "sha512-35o5tIEMLW3JeFJOaaMNR2e5sq+6rpnhrF97PuAxeOm0GlqVTESKhkGj7a5B5mmJSSSU3hUfIhcQCRRsw4Ipzg==", + "dependencies": { + "google-auth-library": "^9.1.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.12.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.12.5.tgz", + "integrity": "sha512-d3iiHxdpg5+ZcJ6jnDSOT8Z0O0VMVGy34jAnYLUX8yd36b1qn8f1TwOA/Lc7TsOh03IkPJ38eGI5qD2EjNkoEA==", + "dependencies": { + "@grpc/proto-loader": "^0.7.13", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.13", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz", + "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@mistralai/mistralai-gcp": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai-gcp/-/mistralai-gcp-1.4.0.tgz", + "integrity": "sha512-QYBbR/T1U4qZ88m6l5RpOlhly2mXWsXi0owicSX6zt6pBaMORxRs6ZRLmaYX5BNjGqxQUl+LtsSwpMtXW2uU2A==", + "dependencies": { + "google-auth-library": "^9.11.0" + }, + "peerDependencies": { + "zod": ">= 3" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz", + "integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", + "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz", + "integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz", + "integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz", + "integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/exporter-zipkin": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz", + "integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz", + "integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz", + "integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/otlp-exporter-base": "0.52.1", + "@opentelemetry/otlp-transformer": "0.52.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz", + "integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", + "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", + "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==", + "dependencies": { + "@opentelemetry/core": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz", + "integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", + "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz", + "integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==", + "dependencies": { + "@opentelemetry/api-logs": "0.52.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/exporter-trace-otlp-grpc": "0.52.1", + "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@opentelemetry/exporter-trace-otlp-proto": "0.52.1", + "@opentelemetry/exporter-zipkin": "1.25.1", + "@opentelemetry/instrumentation": "0.52.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/sdk-logs": "0.52.1", + "@opentelemetry/sdk-metrics": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "@opentelemetry/sdk-trace-node": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz", + "integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", + "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.25.1", + "@opentelemetry/core": "1.25.1", + "@opentelemetry/propagator-b3": "1.25.1", + "@opentelemetry/propagator-jaeger": "1.25.1", + "@opentelemetry/sdk-trace-base": "1.25.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", + "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/resources": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", + "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", + "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "dependencies": { + "@opentelemetry/core": "1.25.1", + "@opentelemetry/resources": "1.25.1", + "@opentelemetry/semantic-conventions": "1.25.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", + "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trpc/server": { + "version": "10.45.0", + "resolved": "https://registry.npmjs.org/@trpc/server/-/server-10.45.0.tgz", + "integrity": "sha512-2Fwzv6nqpE0Ie/G7PeS0EVR89zLm+c1Mw7T+RAGtU807j4oaUx0zGkBXTu5u9AI+j+BYNN2GZxJcuDTAecbr1A==", + "dev": true, + "funding": [ + "https://trpc.io/sponsor" + ] + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "optional": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/caseless": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", + "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==" + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "optional": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "optional": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "optional": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.8.tgz", + "integrity": "sha512-7fx54m60nLFUVYlxAB1xpe9CBWX2vSrk50Y6ogRJ1v5xxtba7qXTg5BgYDN5dq+yuQQ9HaVlHJyAAt1/mxryFg==", + "optional": true, + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "optional": true + }, + "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==", + "optional": true + }, + "node_modules/@types/node": { + "version": "18.19.75", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.75.tgz", + "integrity": "sha512-UIksWtThob6ZVSyxcOqCLOUNg/dyO1Qvx4McgeuhrEtHTLFTf7BBhEazaE4K806FGTPtzd/2sE90qn4fVr7cyw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/qs": { + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", + "optional": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "optional": true + }, + "node_modules/@types/request": { + "version": "2.48.12", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.12.tgz", + "integrity": "sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==", + "dependencies": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "node_modules/@types/request/node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "optional": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "optional": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==" + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "dev": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "dev": true, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "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==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "optional": true, + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/big.js": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.2.tgz", + "integrity": "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==", + "optional": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bigjs" + } + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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==", + "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==" + }, + "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==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deeks": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/deeks/-/deeks-3.1.0.tgz", + "integrity": "sha512-e7oWH1LzIdv/prMQ7pmlDlaVoL64glqzvNgkgQNgyec9ORPHrT2jaOqMtRyqJuwWjtfb6v+2rk9pmaHj+F137A==", + "dev": true, + "engines": { + "node": ">= 16" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/doc-path": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-4.1.1.tgz", + "integrity": "sha512-h1ErTglQAVv2gCnOpD3sFS6uolDbOKHDU1BZq+Kl3npPqroU3dYL42lUgMfd5UimlwtRgp7C9dLGwqQ5D2HYgQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotprompt": { + "version": "1.0.0-dev.3", + "resolved": "https://registry.npmjs.org/dotprompt/-/dotprompt-1.0.0-dev.3.tgz", + "integrity": "sha512-gAcIhG+vzZlrZcBN/lO8gEMUtXHKMLbs1+snJuZrPkZnNgnWrzz1hYN4vYotpng/5fHsYSPUEckECy6qUNSrsQ==", + "dependencies": { + "handlebars": "^4.7.8", + "yaml": "^2.5.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "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==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/extract-zip/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==", + "dev": true + }, + "node_modules/farmhash-modern": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz", + "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==", + "optional": true, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fast-xml-parser": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz", + "integrity": "sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "optional": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/firebase-admin": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-13.0.2.tgz", + "integrity": "sha512-YWVpoN+tZVSRXF0qC0gojoF5bSqvBRbnBk8+xUtFiguM2L4vB7f0moAwV1VVWDDHvTnvQ68OyTMpdp6wKo/clw==", + "optional": true, + "dependencies": { + "@fastify/busboy": "^3.0.0", + "@firebase/database-compat": "^2.0.0", + "@firebase/database-types": "^1.0.6", + "@types/node": "^22.8.7", + "farmhash-modern": "^1.1.0", + "google-auth-library": "^9.14.2", + "jsonwebtoken": "^9.0.0", + "jwks-rsa": "^3.1.0", + "node-forge": "^1.3.1", + "uuid": "^11.0.2" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@google-cloud/firestore": "^7.11.0", + "@google-cloud/storage": "^7.14.0" + } + }, + "node_modules/firebase-admin/node_modules/@types/node": { + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", + "optional": true, + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/firebase-admin/node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "optional": true + }, + "node_modules/firebase-admin/node_modules/uuid": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "optional": true, + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formdata-node/node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "devOptional": true + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/genkit": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.15.tgz", + "integrity": "sha512-2dFUQ4qnXxv98ijs8/dr3yjof7Fev+FT+alxuRwXxUhpqGc4ei/0p1xgtFeq5j6sWBoRcUHNvzCPrMwgDDd4pA==", + "dependencies": { + "@genkit-ai/ai": "1.0.0-rc.15", + "@genkit-ai/core": "1.0.0-rc.15", + "uuid": "^10.0.0" + } + }, + "node_modules/genkit-cli": { + "version": "1.0.0-rc.15", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.15.tgz", + "integrity": "sha512-OADTCKQ97MUWBPsS2NknSQWQjQ1FKhCiFa/BXFRchib1k3gC4bYtAVjRADPg/jn1STjNuOmCblTru6/IDiUaEw==", + "dev": true, + "dependencies": { + "@genkit-ai/telemetry-server": "1.0.0-rc.15", + "@genkit-ai/tools-common": "1.0.0-rc.15", + "axios": "^1.7.7", + "colorette": "^2.0.20", + "commander": "^11.1.0", + "extract-zip": "^2.0.1", + "get-port": "5.1.1", + "inquirer": "^8.2.0", + "open": "^6.3.0", + "ora": "^5.4.1" + }, + "bin": { + "genkit": "dist/bin/genkit.js" + } + }, + "node_modules/genkit/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", + "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/googleapis": { + "version": "140.0.1", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-140.0.1.tgz", + "integrity": "sha512-ZGvBX4mQcFXO9ACnVNg6Aqy3KtBPB5zTuue43YVLxwn8HSv8jB7w+uDKoIPSoWuxGROgnj2kbng6acXncOQRNA==", + "dependencies": { + "google-auth-library": "^9.0.0", + "googleapis-common": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis-common": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.2.0.tgz", + "integrity": "sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==", + "dependencies": { + "extend": "^3.0.2", + "gaxios": "^6.0.3", + "google-auth-library": "^9.7.0", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "optional": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz", + "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==", + "optional": true + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/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==" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/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==" + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/import-in-the-middle": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.12.0.tgz", + "integrity": "sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-2-csv": { + "version": "5.5.8", + "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-5.5.8.tgz", + "integrity": "sha512-eMQHOwV+av8Sgo+fkbEbQWOw/kwh89AZ5fNA8TYfcooG6TG1ZOL2WcPUrngIMIK8dBJitQ8QEU0zbncQ0CX4CQ==", + "dev": true, + "dependencies": { + "deeks": "3.1.0", + "doc-path": "4.1.1" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "optional": true, + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "optional": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "optional": true, + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/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==", + "optional": true + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", + "optional": true, + "dependencies": { + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", + "debug": "^4.3.4", + "jose": "^4.14.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jwks-rsa/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/jwks-rsa/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==", + "optional": true + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", + "optional": true + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "optional": true + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "optional": true + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "optional": true + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "optional": true + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "optional": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "optional": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "optional": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "optional": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dev": true, + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/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==", + "dev": true + }, + "node_modules/long": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-memoizer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", + "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", + "optional": true, + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "6.0.0" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "optional": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/openai": { + "version": "4.82.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.82.0.tgz", + "integrity": "sha512-1bTxOVGZuVGsKKUWbh3BEwX1QxIXUftJv+9COhhGGVDTFwiaOd4gWsMynF2ewj1mg6by3/O+U8+EEHpWRdPaJg==", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/openapi3-ts": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.4.0.tgz", + "integrity": "sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==", + "dev": true, + "dependencies": { + "yaml": "^2.5.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "optional": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/partial-json": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", + "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "dev": true, + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/protobuf.js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/protobuf.js/-/protobuf.js-1.1.2.tgz", + "integrity": "sha512-USO7Xus/pzPw549M1TguiyoOrKEhm9VMXv+CkDufcjMC8Rd7EPbxeRQPEjCV8ua1tm0k7z9xHkogcxovZogWdA==", + "dependencies": { + "long": "~1.1.2" + } + }, + "node_modules/protobuf.js/node_modules/long": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/long/-/long-1.1.5.tgz", + "integrity": "sha512-TU6nAF5SdasnTr28c7e74P4Crbn9o3/zwo1pM22Wvg2i2vlZ4Eelxwu4QT7j21z0sDBlJDEnEZjXTZg2J8WJrg==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/ps-tree": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz", + "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==", + "dev": true, + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.0.tgz", + "integrity": "sha512-/Tvpny/RVVicqlYTKwt/GtpZRsPG1CmJNhxVKGz+Sy/4MONfXCVNK69MFgGKdUt0/324q3ClI2dICcPgISrC8g==", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/require-in-the-middle/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/require-in-the-middle/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==" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-request": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz", + "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==", + "dependencies": { + "@types/request": "^2.48.8", + "extend": "^3.0.2", + "teeny-request": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/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==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "dependencies": { + "duplexer": "~0.1.1" + } + }, + "node_modules/stream-events": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz", + "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==", + "dependencies": { + "stubs": "^3.0.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "optional": true + }, + "node_modules/stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/teeny-request": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", + "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==", + "dependencies": { + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.9", + "stream-events": "^1.0.5", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/teeny-request/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/teeny-request/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/teeny-request/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/teeny-request/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==" + }, + "node_modules/teeny-request/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/terminate": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/terminate/-/terminate-2.8.0.tgz", + "integrity": "sha512-bcbjJEg0wY5nuJXvGxxHfmoEPkyHLCctUKO6suwtxy7jVSgGcgPeGwpbLDLELFhIaxCGRr3dPvyNg1yuz2V0eg==", + "dev": true, + "dependencies": { + "ps-tree": "^1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "dev": true, + "engines": { + "node": ">= 14.0.0" + } + }, + "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==" + }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==" + }, + "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==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "optional": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "optional": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "dev": true, + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dev": true, + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "optional": true + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", + "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} diff --git a/samples/js-angular/server/package.json b/samples/js-angular/server/package.json index 428c00d1c..338e82f2f 100644 --- a/samples/js-angular/server/package.json +++ b/samples/js-angular/server/package.json @@ -14,13 +14,14 @@ "author": "", "license": "ISC", "dependencies": { - "genkit": "^0.9.0-rc || ^0.9", - "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9", + "genkit": "^1.0.0-rc.15", + "@genkit-ai/vertexai": "^1.0.0-rc.15", + "@genkit-ai/express": "^1.0.0-rc.15", "express": "^4.21.0", "partial-json": "^0.1.7" }, "devDependencies": { - "genkit-cli": "^0.9.0-rc || ^0.9", + "genkit-cli": "^1.0.0-rc.15", "typescript": "^5.4.5", "tsx": "^4.19.2" } diff --git a/samples/js-angular/server/src/agent.ts b/samples/js-angular/server/src/agent.ts index 3ee2f03b5..9a98e5cef 100644 --- a/samples/js-angular/server/src/agent.ts +++ b/samples/js-angular/server/src/agent.ts @@ -21,7 +21,6 @@ import { ModelArgument, PartSchema, ToolArgument, - run, z, } from 'genkit'; import { GenerateResponseSchema } from 'genkit/model'; @@ -64,7 +63,7 @@ export function defineAgent( return ai.defineFlow( { name, inputSchema: AgentInput, outputSchema: GenerateResponseSchema }, async (request, streamingCallback) => { - const history = await run( + const history = await ai.run( 'retrieve-history', request.conversationId, async () => { @@ -96,7 +95,7 @@ export function defineAgent( returnToolRequests, streamingCallback, }); - await run( + await ai.run( 'save-history', { conversationId: request.conversationId, history: resp.messages }, async () => { diff --git a/samples/js-angular/server/src/index.ts b/samples/js-angular/server/src/index.ts index e5b080060..b4b53da26 100644 --- a/samples/js-angular/server/src/index.ts +++ b/samples/js-angular/server/src/index.ts @@ -14,10 +14,10 @@ * limitations under the License. */ +import { startFlowServer } from '@genkit-ai/express'; import { chatbotFlow } from './chatbot.js'; -import { ai } from './genkit.js'; import { streamCharacters } from './jsonStreaming.js'; -ai.startFlowServer({ +startFlowServer({ flows: [chatbotFlow, streamCharacters], }); From 2a50da3162b9e6208cc57a18ae5ec9b1f65953ac Mon Sep 17 00:00:00 2001 From: Sam Phillips Date: Tue, 4 Feb 2025 12:28:02 -0500 Subject: [PATCH 529/562] feat: Add dev.nix for js-menu (#1812) * feat: Add dev.nix for js-menu --- samples/idx-template.json | 3 +- samples/js-chatbot/README.md | 2 +- samples/js-menu/.gitignore | 1 + samples/js-menu/.idx/dev.nix | 37 +++ samples/js-menu/README.md | 17 +- samples/js-menu/package-lock.json | 451 ++++++++++++------------------ samples/js-menu/src/index.ts | 4 + 7 files changed, 234 insertions(+), 281 deletions(-) create mode 100644 samples/js-menu/.gitignore create mode 100644 samples/js-menu/.idx/dev.nix diff --git a/samples/idx-template.json b/samples/idx-template.json index dc88e63cb..2d8575e2e 100644 --- a/samples/idx-template.json +++ b/samples/idx-template.json @@ -17,7 +17,8 @@ "js-character-generator": "Simple Character Generator", "js-coffee-shop": "Coffeeshop w Several types of Prompt", "js-schoolAgent": "A Multi-Agent School Assistant", - "js-prompts": "A sample with various prompt styles" + "js-prompts": "A sample with various prompt styles", + "js-menu": "Menu parsing with increasing levels of complex architectures" } } ] diff --git a/samples/js-chatbot/README.md b/samples/js-chatbot/README.md index 34ece718b..8451f7465 100644 --- a/samples/js-chatbot/README.md +++ b/samples/js-chatbot/README.md @@ -4,7 +4,7 @@ This is a simple chatbot. You can pick which model to use. Prerequisite -- Google Cloud project with Vertex AI API enabled (https://pantheon.corp.google.com/apis/library/aiplatform.googleapis.com) +- Google Cloud project with Vertex AI API enabled (https://cloud.google.com/apis/library/aiplatform.googleapis.com) - gcloud CLI installed (https://cloud.google.com/sdk/docs/install-sdk) - to use Llama 3.1 405b enable it in the Vertex AI [Model Garden](https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama3-405b-instruct-maas) diff --git a/samples/js-menu/.gitignore b/samples/js-menu/.gitignore new file mode 100644 index 000000000..63d449b73 --- /dev/null +++ b/samples/js-menu/.gitignore @@ -0,0 +1 @@ +__db_menu-items.json \ No newline at end of file diff --git a/samples/js-menu/.idx/dev.nix b/samples/js-menu/.idx/dev.nix new file mode 100644 index 000000000..11313f66e --- /dev/null +++ b/samples/js-menu/.idx/dev.nix @@ -0,0 +1,37 @@ +## Default Nix Environment for Typescript + Gemini Examples +## Requires the sample to be started with npx run genkit:dev + +# To learn more about how to use Nix to configure your environment +# see: https://developers.google.com/idx/guides/customize-idx-env +{ pkgs, ... }: { + # Which nixpkgs channel to use. + channel = "stable-24.05"; # or "unstable" + # Use https://search.nixos.org/packages to find packages + packages = [ + pkgs.nodejs_20 + pkgs.util-linux + ]; + # Sets environment variables in the workspace + env = { + #TODO Get a API key from https://g.co/ai/idxGetGeminiKey + GOOGLE_GENAI_API_KEY = ""; + }; + idx = { + # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" + extensions = [ + ]; + + # Workspace lifecycle hooks + workspace = { + # Runs when a workspace is first created + onCreate = { + npm-install = "npm ci --no-audit --prefer-offline --no-progress --timing"; + default.openFiles = [ "README.md" "src/index.ts" ]; + }; + # Runs when the workspace is (re)started + onStart = { + run-server = "npm run genkit:dev"; + }; + }; + }; +} \ No newline at end of file diff --git a/samples/js-menu/README.md b/samples/js-menu/README.md index 982ea26a8..be72f0e2e 100644 --- a/samples/js-menu/README.md +++ b/samples/js-menu/README.md @@ -8,7 +8,11 @@ To test each one out, open the Developer UI and exercise the prompts and flows. ## Prerequisites -This example uses Vertex AI for language models and embeddings. +Prerequisite + +- Google Cloud project with Vertex AI API enabled (https://console.cloud.google.com/apis/library/aiplatform.googleapis.com) +- gcloud CLI installed (https://cloud.google.com/sdk/docs/install-sdk) +- to use Llama 3.1 405b enable it in the Vertex AI [Model Garden](https://console.cloud.google.com/vertex-ai/publishers/meta/model-garden/llama3-405b-instruct-maas) ## Prompts and Flows @@ -21,7 +25,16 @@ This example uses Vertex AI for language models and embeddings. ## Running the sample +### Setup authentication (can skip on IDX) + +```bash +gcloud auth login +gcloud auth application-default login --project YOUR_PROJECT +``` + +### Run the sample + ```bash npm i -genkit start +npm run genkit:dev ``` diff --git a/samples/js-menu/package-lock.json b/samples/js-menu/package-lock.json index 0143c09f7..22353a0ab 100644 --- a/samples/js-menu/package-lock.json +++ b/samples/js-menu/package-lock.json @@ -9,14 +9,14 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@genkit-ai/dev-local-vectorstore": "^0.9.0-rc || ^0.9", - "@genkit-ai/evaluator": "^0.9.0-rc || ^0.9", - "@genkit-ai/firebase": "^0.9.0-rc || ^0.9", - "@genkit-ai/vertexai": "^0.9.0-rc || ^0.9", - "genkit": "^0.9.0-rc || ^0.9" + "@genkit-ai/dev-local-vectorstore": "^1.0.0-rc.12", + "@genkit-ai/evaluator": "^1.0.0-rc.12", + "@genkit-ai/firebase": "^1.0.0-rc.12", + "@genkit-ai/vertexai": "^1.0.0-rc.12", + "genkit": "^1.0.0-rc.12" }, "devDependencies": { - "genkit-cli": "^0.9.0-rc || ^0.9", + "genkit-cli": "^1.0.0-rc.12", "rimraf": "^6.0.1", "typescript": "^5.3.3" } @@ -36,14 +36,6 @@ "web-streams-polyfill": "^3.2.1" } }, - "node_modules/@anthropic-ai/sdk/node_modules/@types/node": { - "version": "18.19.74", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", - "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, "node_modules/@anthropic-ai/sdk/node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -63,11 +55,6 @@ } } }, - "node_modules/@anthropic-ai/sdk/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, "node_modules/@anthropic-ai/vertex-sdk": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@anthropic-ai/vertex-sdk/-/vertex-sdk-0.4.3.tgz", @@ -588,24 +575,50 @@ } }, "node_modules/@genkit-ai/ai": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-0.9.12.tgz", - "integrity": "sha512-xyVVAIGKNpj5zCkoEfWZkzwctl0/hmpX6vKZgdgMH2MiqP5LzTp7rUekBMon8c1rMDVAze97QVSjAmZIoMLSlA==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/ai/-/ai-1.0.0-rc.14.tgz", + "integrity": "sha512-5CHyRixumiM5uHkzwHl7IWbrYSzWvhDvYAsMoq9mmI8iCJA9g/3u8JOcHSRiseuaap5gv/QGm809IpIlak55ZA==", "dependencies": { - "@genkit-ai/core": "0.9.12", + "@genkit-ai/core": "1.0.0-rc.14", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.11.19", "colorette": "^2.0.20", + "dotprompt": "^1.0.0-dev.3 || ^1", "json5": "^2.2.3", "node-fetch": "^3.3.2", "partial-json": "^0.1.7", "uuid": "^10.0.0" } }, + "node_modules/@genkit-ai/ai/node_modules/@types/node": { + "version": "20.17.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.17.tgz", + "integrity": "sha512-/WndGO4kIfMicEQLTi/mDANUu/iVUhT7KboZPdEqqHQ4aTS+3qT3U5gIqWDFV+XouorjfgGqvKILJeHhuQgFYg==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@genkit-ai/ai/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/@genkit-ai/ai/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@genkit-ai/core": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-0.9.12.tgz", - "integrity": "sha512-QPJZ3TL5Iq2fyeo30MpUjd3ZLcYQf97RsitDZhMbGy3vMwbgig0nhEbJ6v/qaWsOMqSfIxJE/gETY3mMts1vRg==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/core/-/core-1.0.0-rc.14.tgz", + "integrity": "sha512-8h0Vl3SCP0vYIN60uELaZWEAP6BjLxXTcoWv2MFn7khxblzVkMWq98IxPThHo3s5BGB+Pxnhnsywdb/ItliFUA==", "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -613,11 +626,13 @@ "@opentelemetry/sdk-metrics": "^1.25.0", "@opentelemetry/sdk-node": "^0.52.0", "@opentelemetry/sdk-trace-base": "^1.25.0", + "@types/json-schema": "^7.0.15", "ajv": "^8.12.0", "ajv-formats": "^3.0.1", "async-mutex": "^0.5.0", "body-parser": "^1.20.3", "cors": "^2.8.5", + "dotprompt": "^1.0.0-dev.3 || ^1", "express": "^4.21.0", "get-port": "^5.1.0", "json-schema": "^0.4.0", @@ -626,49 +641,51 @@ } }, "node_modules/@genkit-ai/dev-local-vectorstore": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/dev-local-vectorstore/-/dev-local-vectorstore-0.9.12.tgz", - "integrity": "sha512-wkrLRqTw1N50hZpQlC1Hq8yOWapyz3BiC03auK93j85sL2L6tIFQ1zFHw7qKD+t39fiTmpocl8O6spLEerUKoA==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/dev-local-vectorstore/-/dev-local-vectorstore-1.0.0-rc.14.tgz", + "integrity": "sha512-oOAtMIg+W5+++qkg1NZbd/oK4vuJzz4D/CnrF/TJGN9I2ytq+7WE0FE8lL09hIFu1LINcogGVtir7mkBCgm4MA==", "dependencies": { "compute-cosine-similarity": "^1.1.0", "ts-md5": "^1.3.1" }, "peerDependencies": { - "genkit": "0.9.12" - } - }, - "node_modules/@genkit-ai/dotprompt": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/dotprompt/-/dotprompt-0.9.12.tgz", - "integrity": "sha512-eEHBRzRVemiPuqCBbXiLgltNWpmCHmC+gVHBhsAnrfOYBlwmPvh2nnAPBXGYnkDH87PuN11jg8YJQmO4kQuoSw==", - "dependencies": { - "@genkit-ai/ai": "0.9.12", - "@genkit-ai/core": "0.9.12", - "front-matter": "^4.0.2", - "handlebars": "^4.7.8", - "node-fetch": "^3.3.2" + "genkit": "^1.0.0-rc.14" } }, "node_modules/@genkit-ai/evaluator": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/evaluator/-/evaluator-0.9.12.tgz", - "integrity": "sha512-JbZfwwqDf6IP84cGt+QZlSB0h+FcdAM7Ur6A/lL5GJdMudIjRdxmhu3FEu/kWKK59EQlJShGBW5/Vyw93ynPqw==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/evaluator/-/evaluator-1.0.0-rc.14.tgz", + "integrity": "sha512-P/3rfbI15IMuxuvsARbiYh+2tMBQFAtG/uxxt4BqDHw694O/DTctYoahIaw9+hPiAAYOtQ/asSsCIo73U3Uhxw==", "dependencies": { - "@genkit-ai/dotprompt": "0.9.12", "compute-cosine-similarity": "^1.1.0", + "dotprompt": "^1.0.0-dev.3 || ^1", "node-fetch": "^3.3.2", "path": "^0.12.7" }, "peerDependencies": { - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.14" + } + }, + "node_modules/@genkit-ai/express": { + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/express/-/express-1.0.0-rc.14.tgz", + "integrity": "sha512-I8VcuV2iyJXs/X3nCQZ/UR+FIO7XwaBo6+KWSDnugJrCweQZQ0LCYHbinSp1obLm/xCML7CWnqpZTX87mnbe3w==", + "dependencies": { + "body-parser": "^1.20.3", + "cors": "^2.8.5" + }, + "peerDependencies": { + "express": "^4.21.1", + "genkit": "^1.0.0-rc.14" } }, "node_modules/@genkit-ai/firebase": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/firebase/-/firebase-0.9.12.tgz", - "integrity": "sha512-PbEAPO3GTENaJR5RUqrq88YIVxgkVi2pc82kOCrkJDpw/+PcyRIZcgvbQYSAzB932yVU0sy33zzdUfg9t2thMA==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/firebase/-/firebase-1.0.0-rc.14.tgz", + "integrity": "sha512-bTbq16E6gUhzn6veClz+zJ6i79QwQg7Sf2c8gN0OHDUDkgbagJP3GcloGVPCh9tDO3KN7hWuUX0xNslmjIbSsA==", "dependencies": { - "@genkit-ai/google-cloud": "0.9.12", + "@genkit-ai/express": "^1.0.0-rc.14", + "@genkit-ai/google-cloud": "^1.0.0-rc.14", "express": "^4.21.0", "google-auth-library": "^9.6.3" }, @@ -676,13 +693,13 @@ "@google-cloud/firestore": "^7.6.0", "firebase-admin": ">=12.2", "firebase-functions": ">=4.8", - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.14" } }, "node_modules/@genkit-ai/google-cloud": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/google-cloud/-/google-cloud-0.9.12.tgz", - "integrity": "sha512-hYDvdQmGdI/JC6hUa6hMs6+Kc04wmi+/54zJIB+EOiNLd6fJ193B+q/pHqI4UxvofJPsf9C6VTK5Q54+yyQm7w==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/google-cloud/-/google-cloud-1.0.0-rc.14.tgz", + "integrity": "sha512-rPUR//+V9yNJbZJey+aSAKIKfdFfN7jhb5gUkxXjYf+erAAukt2PW9ltDzYKkdESZWvtQvPx/8/keWX28hDZJQ==", "dependencies": { "@google-cloud/logging-winston": "^6.0.0", "@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.19.0", @@ -703,17 +720,17 @@ "winston": "^3.12.0" }, "peerDependencies": { - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.14" } }, "node_modules/@genkit-ai/telemetry-server": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-0.9.12.tgz", - "integrity": "sha512-8w1O9LOUtMCGc8bx+Vcd5D68L8ctM6H544Vxnfvy8gx4JHV5dj7vQiI9YDgm65pgP15pytQzt1TcRtOtYoimFw==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/telemetry-server/-/telemetry-server-1.0.0-rc.14.tgz", + "integrity": "sha512-cFxLS8IkXhRds9+wgaru3QirgehQImG9CuhLfjo09MRcIM21Vr9F083YVnlO8WDckWz5NwclkfFKkD25FDpE/w==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", - "@genkit-ai/tools-common": "0.9.12", + "@genkit-ai/tools-common": "1.0.0-rc.14", "@google-cloud/firestore": "^7.6.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.25.0", @@ -727,14 +744,16 @@ } }, "node_modules/@genkit-ai/tools-common": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-0.9.12.tgz", - "integrity": "sha512-A9ToM/CY6vxcBSw4o47Q7qrpMaRsk4P6ZuzoDMlCzCVI+RZN5ulTL4LXMrq6nQ/KxY1GT94bSoulROXBMzW/Gw==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/tools-common/-/tools-common-1.0.0-rc.14.tgz", + "integrity": "sha512-50PE/CIL3u9YC4c3vifz6CVSlC1cmQ3xAHMOsUxsRRvcPzIbL8jOCYMKAnbdgAkp0WGgWG+fLjiKbUPZydEgcg==", "dev": true, "dependencies": { "@asteasolutions/zod-to-openapi": "^7.0.0", "@trpc/server": "10.45.0", "adm-zip": "^0.5.12", + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", "axios": "^1.7.7", "body-parser": "^1.20.2", "chokidar": "^3.5.3", @@ -757,46 +776,15 @@ "zod-to-json-schema": "^3.22.4" } }, - "node_modules/@genkit-ai/tools-common/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==", - "dev": true - }, - "node_modules/@genkit-ai/tools-common/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@genkit-ai/tools-common/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@genkit-ai/vertexai": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/@genkit-ai/vertexai/-/vertexai-0.9.12.tgz", - "integrity": "sha512-lA6lwh5jgYgFr7RsFCXs+iASUZI2CmrGQEJCR+/qKJTNG0OyI4O7CdoT5SZSc1fK7oXeWH0/4x6mIwcngjy2Gw==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@genkit-ai/vertexai/-/vertexai-1.0.0-rc.14.tgz", + "integrity": "sha512-7j3xV9wdqIhjUjvy6cogIaTATl28z+wjiP9hLfeYb6XOyG7HFve5jZ4xzDHK2pGVOjblrP2XDyahOE2BUK6fyQ==", "dependencies": { "@anthropic-ai/sdk": "^0.24.3", "@anthropic-ai/vertex-sdk": "^0.4.0", "@google-cloud/aiplatform": "^3.23.0", - "@google-cloud/vertexai": "^1.9.0", + "@google-cloud/vertexai": "^1.9.2", "@mistralai/mistralai-gcp": "^1.3.5", "google-auth-library": "^9.14.2", "googleapis": "^140.0.1", @@ -808,7 +796,7 @@ "firebase-admin": ">=12.2" }, "peerDependencies": { - "genkit": "0.9.12" + "genkit": "^1.0.0-rc.14" } }, "node_modules/@google-cloud/aiplatform": { @@ -845,19 +833,6 @@ "node": ">=14.0.0" } }, - "node_modules/@google-cloud/bigquery/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "optional": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@google-cloud/common": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-5.0.2.tgz", @@ -935,18 +910,6 @@ "winston": ">=3.2.1" } }, - "node_modules/@google-cloud/logging/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/@google-cloud/opentelemetry-cloud-monitoring-exporter": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/@google-cloud/opentelemetry-cloud-monitoring-exporter/-/opentelemetry-cloud-monitoring-exporter-0.19.0.tgz", @@ -1086,9 +1049,9 @@ } }, "node_modules/@google-cloud/vertexai": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@google-cloud/vertexai/-/vertexai-1.9.2.tgz", - "integrity": "sha512-pJSUG3r5QIvCFNfkz7/y7kEqvEJaVAk0jZbZoKbcPCRUnXaUeAq7p8I0oklqetGyxbUcZ2FOGpt+Y+4uIltVPg==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@google-cloud/vertexai/-/vertexai-1.9.3.tgz", + "integrity": "sha512-35o5tIEMLW3JeFJOaaMNR2e5sq+6rpnhrF97PuAxeOm0GlqVTESKhkGj7a5B5mmJSSSU3hUfIhcQCRRsw4Ipzg==", "dependencies": { "google-auth-library": "^9.1.0" }, @@ -1321,9 +1284,9 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", - "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", + "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", "engines": { "node": ">=14" }, @@ -2849,17 +2812,6 @@ "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, - "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", - "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, "node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/core": { "version": "1.25.1", "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", @@ -3087,6 +3039,11 @@ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, "node_modules/@types/jsonwebtoken": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.8.tgz", @@ -3128,11 +3085,11 @@ } }, "node_modules/@types/node": { - "version": "20.17.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", - "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", + "version": "18.19.75", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.75.tgz", + "integrity": "sha512-UIksWtThob6ZVSyxcOqCLOUNg/dyO1Qvx4McgeuhrEtHTLFTf7BBhEazaE4K806FGTPtzd/2sE90qn4fVr7cyw==", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~5.26.4" } }, "node_modules/@types/node-fetch": { @@ -3401,12 +3358,10 @@ } }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/array-flatten": { "version": "1.1.1", @@ -3692,9 +3647,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==" + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==" }, "node_modules/cli-cursor": { "version": "3.1.0", @@ -4058,6 +4013,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/dotprompt": { + "version": "1.0.0-dev.3", + "resolved": "https://registry.npmjs.org/dotprompt/-/dotprompt-1.0.0-dev.3.tgz", + "integrity": "sha512-gAcIhG+vzZlrZcBN/lO8gEMUtXHKMLbs1+snJuZrPkZnNgnWrzz1hYN4vYotpng/5fHsYSPUEckECy6qUNSrsQ==", + "dependencies": { + "handlebars": "^4.7.8", + "yaml": "^2.5.0" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -4221,18 +4185,6 @@ "node": ">=0.8.0" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -4556,9 +4508,9 @@ } }, "node_modules/firebase-admin/node_modules/@types/node": { - "version": "22.12.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz", - "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", + "version": "22.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", + "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", "dependencies": { "undici-types": "~6.20.0" } @@ -4581,9 +4533,9 @@ } }, "node_modules/firebase-functions": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-6.3.0.tgz", - "integrity": "sha512-88dRx3dPYvlxIN64H1lJCFV7wGt0cFL0lmXKzVm+rfnkILC0Qt3rMXp3/J/ojf2StclBwJBu2QZ4MRbNYh7B7g==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-6.3.1.tgz", + "integrity": "sha512-LTbmsEkSgaOhzTzGUoF7dv906JJJW89o0/spXgnU8gASyR8JLMrCqwV7FnWLso5hyF0fUqNPaEEw/TzLdZMVXw==", "peer": true, "dependencies": { "@types/cors": "^2.8.5", @@ -4714,14 +4666,6 @@ "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", "dev": true }, - "node_modules/front-matter": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", - "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", - "dependencies": { - "js-yaml": "^3.13.1" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -4783,24 +4727,13 @@ } } }, - "node_modules/gaxios/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/gcp-metadata": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", - "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", "dependencies": { - "gaxios": "^6.0.0", + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" }, "engines": { @@ -4808,24 +4741,23 @@ } }, "node_modules/genkit": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/genkit/-/genkit-0.9.12.tgz", - "integrity": "sha512-m1VQE/yhuii0y1aGTnkoSesSXTNE25q1s7vv5YVgJWa/t2gOXuznZOoHTJ847f/3mKC7fgnV7xGI+t/+7wbe0g==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/genkit/-/genkit-1.0.0-rc.14.tgz", + "integrity": "sha512-JB6fTcJ5CI4UhsP4uNyXpi9/OatyhPwOkBXnbHafE5rdibXuKUXadbb2LC+PWfG3+7WrLXGKlrj7kOIN3Hq2lw==", "dependencies": { - "@genkit-ai/ai": "0.9.12", - "@genkit-ai/core": "0.9.12", - "@genkit-ai/dotprompt": "0.9.12", + "@genkit-ai/ai": "1.0.0-rc.14", + "@genkit-ai/core": "1.0.0-rc.14", "uuid": "^10.0.0" } }, "node_modules/genkit-cli": { - "version": "0.9.12", - "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-0.9.12.tgz", - "integrity": "sha512-Qsk7TmVBxghTT+0cRe2j0fYJboUIfWhuUoeVbWshranye6tConGMyDEZdXBIeMWIixSbG7k8a7a2INdRdjcQJw==", + "version": "1.0.0-rc.14", + "resolved": "https://registry.npmjs.org/genkit-cli/-/genkit-cli-1.0.0-rc.14.tgz", + "integrity": "sha512-JR8Vvm2rGGQMsBkq+dWSypUC4ij1Uew5cjFaS74flyv/glV6RMNMuXP5XQBc5C9zIkfXeT+kng8BghHM4Gx/4g==", "dev": true, "dependencies": { - "@genkit-ai/telemetry-server": "0.9.12", - "@genkit-ai/tools-common": "0.9.12", + "@genkit-ai/telemetry-server": "1.0.0-rc.14", + "@genkit-ai/tools-common": "1.0.0-rc.14", "axios": "^1.7.7", "colorette": "^2.0.20", "commander": "^11.1.0", @@ -4839,6 +4771,18 @@ "genkit": "dist/bin/genkit.js" } }, + "node_modules/genkit/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -5009,16 +4953,12 @@ } } }, - "node_modules/google-gax/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "engines": { + "node": ">=14" } }, "node_modules/googleapis": { @@ -5049,18 +4989,6 @@ "node": ">=14.0.0" } }, - "node_modules/googleapis-common/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -5515,12 +5443,12 @@ } }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -6099,9 +6027,9 @@ } }, "node_modules/openai": { - "version": "4.81.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.81.0.tgz", - "integrity": "sha512-lXkFkV+He3O6RGnldHncRGef4uWHssDsAVwN5I3bWcgIdDPy/w8vgtIAwvZxAj49m4WiwWVD0+eGTJ9xOv/ISA==", + "version": "4.82.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.82.0.tgz", + "integrity": "sha512-1bTxOVGZuVGsKKUWbh3BEwX1QxIXUftJv+9COhhGGVDTFwiaOd4gWsMynF2ewj1mg6by3/O+U8+EEHpWRdPaJg==", "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", @@ -6127,14 +6055,6 @@ } } }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.74", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", - "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, "node_modules/openai/node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -6154,11 +6074,6 @@ } } }, - "node_modules/openai/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, "node_modules/openapi3-ts": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-4.4.0.tgz", @@ -6812,9 +6727,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -7022,11 +6937,6 @@ "node": "*" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -7237,18 +7147,6 @@ } } }, - "node_modules/teeny-request/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/terminate": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/terminate/-/terminate-2.8.0.tgz", @@ -7408,9 +7306,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/unique-string": { "version": "2.0.0", @@ -7464,9 +7362,9 @@ } }, "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -7688,7 +7586,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", - "dev": true, "bin": { "yaml": "bin.mjs" }, diff --git a/samples/js-menu/src/index.ts b/samples/js-menu/src/index.ts index 5776c2268..9d9b746f8 100644 --- a/samples/js-menu/src/index.ts +++ b/samples/js-menu/src/index.ts @@ -34,3 +34,7 @@ export { s05_visionMenuQuestionFlow, } from './05/flows'; export { s05_readMenuPrompt, s05_textMenuPrompt } from './05/prompts'; + +console.log( + 'All prompts and flows loaded, use the Developer UI to test them out' +); From a35927cf192690c369211d433e9de8b05a2e78f6 Mon Sep 17 00:00:00 2001 From: ifielker Date: Tue, 4 Feb 2025 12:47:09 -0500 Subject: [PATCH 530/562] fix: multimodal bugs in Chroma fixed (#1811) * test: add pinecone testing to multimodal testapp * docs: changes for multimodal ai.embed * test: added chromaDb to multimodal testapp --------- Co-authored-by: Ingrid Fielker --- docs/plugins/firebase.md | 52 +++--- docs/plugins/google-genai.md | 4 +- docs/plugins/ollama.md | 6 +- docs/plugins/vertex-ai.md | 2 +- docs/templates/pgvector.md | 4 +- js/plugins/chroma/src/index.ts | 30 +-- .../src/metrics/answer_relevancy.ts | 24 ++- js/plugins/firebase/src/firestoreRetriever.ts | 3 +- .../ollama/tests/embedding_live_test.ts | 10 +- .../vectorsearch/vector_search/retrievers.ts | 12 +- js/testapps/multimodal/src/genkit.ts | 47 ++++- js/testapps/multimodal/src/pdf.ts | 5 - js/testapps/multimodal/src/video.ts | 172 ++++++++++++++++-- 13 files changed, 284 insertions(+), 87 deletions(-) diff --git a/docs/plugins/firebase.md b/docs/plugins/firebase.md index 24a52199f..a173e115a 100644 --- a/docs/plugins/firebase.md +++ b/docs/plugins/firebase.md @@ -5,9 +5,9 @@ use https://github.com/firebase/firebase-tools/blob/master/templates/init/functi The Firebase plugin provides integrations with Firebase services, allowing you to build intelligent and scalable AI applications. Key features include: -- **Firestore Vector Store**: Use Firestore for indexing and retrieval with vector embeddings. -- **Cloud Functions**: Deploy flows as HTTPS-triggered functions. -- **Firebase Authentication**: Implement authorization policies. +- **Firestore Vector Store**: Use Firestore for indexing and retrieval with vector embeddings. +- **Cloud Functions**: Deploy flows as HTTPS-triggered functions. +- **Firebase Authentication**: Implement authorization policies. - **Telemetry**: Export telemetry to [Google's Cloud operations suite](https://cloud.google.com/products/operations) that powers the Firebase Genkit Monitoring console. ## Installation @@ -22,8 +22,8 @@ npm install @genkit-ai/firebase ### Firebase Project Setup -1. All Firebase products require a Firebase project. You can create a new project or enable Firebase in an existing Google Cloud project using the [Firebase console](https://console.firebase.google.com/). -2. If deploying flows with Cloud Functions, [upgrade your Firebase project](https://console.firebase.google.com/project/_/overview?purchaseBillingPlan=metered) to the Blaze plan. +1. All Firebase products require a Firebase project. You can create a new project or enable Firebase in an existing Google Cloud project using the [Firebase console](https://console.firebase.google.com/). +2. If deploying flows with Cloud Functions, [upgrade your Firebase project](https://console.firebase.google.com/project/_/overview?purchaseBillingPlan=metered) to the Blaze plan. 3. If you want to run code locally that exports telemetry, you need the [Google Cloud CLI](https://cloud.google.com/sdk/docs/install) tool installed. ### Firebase Admin SDK Initialization @@ -44,19 +44,19 @@ The plugin requires you to specify your Firebase project ID. You can specify you - Set `projectId` in the `initializeApp()` configuration object as shown in the snippet above. -- Set the `GCLOUD_PROJECT` environment variable. If you're running your flow from a Google Cloud environment (Cloud Functions, Cloud Run, and so on), `GCLOUD_PROJECT` is automatically set to the project ID of the environment. - +- Set the `GCLOUD_PROJECT` environment variable. If you're running your flow from a Google Cloud environment (Cloud Functions, Cloud Run, and so on), `GCLOUD_PROJECT` is automatically set to the project ID of the environment. + If you set `GCLOUD_PROJECT`, you can omit the configuration parameter in `initializeApp()`. ### Credentials To provide Firebase credentials, you also need to set up Google Cloud Application Default Credentials. To specify your credentials: -- If you're running your flow from a Google Cloud environment (Cloud Functions, Cloud Run, and so on), this is set automatically. - -- For other environments: - - 1. Generate service account credentials for your Firebase project and download the JSON key file. You can do so on the [Service account](https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk) page of the Firebase console. +- If you're running your flow from a Google Cloud environment (Cloud Functions, Cloud Run, and so on), this is set automatically. + +- For other environments: + + 1. Generate service account credentials for your Firebase project and download the JSON key file. You can do so on the [Service account](https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk) page of the Firebase console. 2. Set the environment variable `GOOGLE_APPLICATION_CREDENTIALS` to the file path of the JSON file that contains your service account key, or you can set the environment variable `GCLOUD_SERVICE_ACCOUNT_CREDS` to the content of the JSON file. ## Features and usage @@ -158,17 +158,17 @@ const docs = await ai.retrieve({ The following options can be passed to the `options` field in `ai.retrieve`: -- **`limit`**: *(number)* - Specify the maximum number of documents to retrieve. Default is `10`. - -- **`where`**: *(Record\)* +- **`limit`**: *(number)* + Specify the maximum number of documents to retrieve. Default is `10`. + +- **`where`**: *(Record\)* Add additional filters based on Firestore fields. Example: ```js where: { category: 'news', status: 'published' } ``` -- **`collection`**: *(string)* +- **`collection`**: *(string)* Override the default collection specified in the retriever configuration. This is useful for querying subcollections or dynamically switching between collections. #### Populate Firestore with Embeddings @@ -220,10 +220,10 @@ export async function indexMenu(filePath: string) { async function indexToFirestore(data: string[]) { for (const text of data) { - const embedding = await ai.embed({ + const embedding = (await ai.embed({ embedder: indexConfig.embedder, content: text, - }); + }))[0].embedding; await firestore.collection(indexConfig.collection).add({ [indexConfig.vectorField]: FieldValue.vector(embedding), [indexConfig.contentField]: text, @@ -243,8 +243,8 @@ Firestore depends on indexes to provide fast and efficient querying on collectio The prior example requires the `embedding` field to be indexed to work. To create the index: -- Run the `gcloud` command described in the [Create a single-field vector index](https://firebase.google.com/docs/firestore/vector-search?authuser=0#create_and_manage_vector_indexes) section of the Firestore docs. - +- Run the `gcloud` command described in the [Create a single-field vector index](https://firebase.google.com/docs/firestore/vector-search?authuser=0#create_and_manage_vector_indexes) section of the Firestore docs. + The command looks like the following: ``` @@ -260,7 +260,7 @@ The prior example requires the `embedding` field to be indexed to work. To creat #### Learn more -- See the [Retrieval-augmented generation](http://../rag.md) page for a general discussion on indexers and retrievers in Genkit. +- See the [Retrieval-augmented generation](http://../rag.md) page for a general discussion on indexers and retrievers in Genkit. - See [Search with vector embeddings](https://firebase.google.com/docs/firestore/vector-search) in the Cloud Firestore docs for more on the vector search feature. ### Deploy flows as Cloud Functions @@ -314,10 +314,10 @@ The `onFlow()` function has some options not present in `defineFlow()`: ); ``` -- `enforceAppCheck`: when `true`, reject requests with missing or invalid [App Check](https://firebase.google.com/docs/app-check) tokens. - -- `consumeAppCheckToken`: when `true`, invalidate the App Check token after verifying it. - +- `enforceAppCheck`: when `true`, reject requests with missing or invalid [App Check](https://firebase.google.com/docs/app-check) tokens. + +- `consumeAppCheckToken`: when `true`, invalidate the App Check token after verifying it. + See [Replay protection](https://firebase.google.com/docs/app-check/cloud-functions#replay-protection). ### Firebase Authentication diff --git a/docs/plugins/google-genai.md b/docs/plugins/google-genai.md index b758d1a94..8202c20ce 100644 --- a/docs/plugins/google-genai.md +++ b/docs/plugins/google-genai.md @@ -67,7 +67,7 @@ const ai = genkit({ plugins: [googleAI()], }); -const embedding = await ai.embed({ +const embeddings = await ai.embed({ embedder: textEmbedding004, content: input, }); @@ -285,6 +285,6 @@ const analyzeVideoResponse = await ai.generate({ Only specific models, such as `gemini15Flash` and `gemini15Pro`, support context caching. If an unsupported model is used, an error will be raised, indicating that caching cannot be applied. -### Further Reading +### Further Reading See more information regarding context caching on Google AI in their [documentation](https://ai.google.dev/gemini-api/docs/caching?lang=node). diff --git a/docs/plugins/ollama.md b/docs/plugins/ollama.md index 5b6ecd385..c43ae4ef8 100644 --- a/docs/plugins/ollama.md +++ b/docs/plugins/ollama.md @@ -140,13 +140,13 @@ const ai = genkit({ ], }); -async function getEmbedding() { - const embedding = await ai.embed({ +async function getEmbeddings() { + const embeddings = await ai.embed({ embedder: 'ollama/nomic-embed-text', content: 'Some text to embed!', }) - return embedding; + return embeddings; } getEmbedding().then((e) => console.log(e)) diff --git a/docs/plugins/vertex-ai.md b/docs/plugins/vertex-ai.md index fb06f5c89..44c09e471 100644 --- a/docs/plugins/vertex-ai.md +++ b/docs/plugins/vertex-ai.md @@ -137,7 +137,7 @@ const ai = genkit({ plugins: [vertexAI({ location: 'us-central1' })], }); -const embedding = await ai.embed({ +const embeddings = await ai.embed({ embedder: textEmbedding004, content: 'How many widgets do you have in stock?', }); diff --git a/docs/templates/pgvector.md b/docs/templates/pgvector.md index 52fa35868..4c8e3593a 100644 --- a/docs/templates/pgvector.md +++ b/docs/templates/pgvector.md @@ -27,10 +27,10 @@ const sqlRetriever = ai.defineRetriever( configSchema: QueryOptions, }, async (input, options) => { - const embedding = await ai.embed({ + const embedding = (await ai.embed({ embedder: textEmbedding004, content: input, - }); + }))[0].embedding; const results = await sql` SELECT episode_id, season_number, chunk as content FROM embeddings diff --git a/js/plugins/chroma/src/index.ts b/js/plugins/chroma/src/index.ts index 1187aaa95..7fd803cd0 100644 --- a/js/plugins/chroma/src/index.ts +++ b/js/plugins/chroma/src/index.ts @@ -142,7 +142,7 @@ export function chromaRetriever( }); } - const embedding = await ai.embed({ + const queryEmbeddings = await ai.embed({ embedder, content, options: embedderOptions, @@ -152,7 +152,7 @@ export function chromaRetriever( include: getIncludes(options?.include), where: options?.where, whereDocument: options?.whereDocument, - queryEmbeddings: embedding[0].embedding, + queryEmbeddings: queryEmbeddings[0].embedding, }); const documents = results.documents[0]; @@ -177,8 +177,11 @@ export function chromaRetriever( return { documents: combined.map((result) => { const data = result.document; - const dataType = result.metadata.datatype; - const docMetadata = JSON.parse(result.metadata.docMetadata); + const metadata = result.metadata.metadata[0]; + const dataType = metadata.dataType; + const docMetadata = metadata.docMetadata + ? JSON.parse(metadata.docMetadata) + : undefined; return Document.fromData(data, dataType, docMetadata).toJSON(); }), }; @@ -280,7 +283,7 @@ export function chromaIndexer( const embeddingDocs = doc.getEmbeddingDocuments(docEmbeddings); return docEmbeddings.map((docEmbedding, j) => { const metadata: Metadata = { - docMetdata: JSON.stringify(embeddingDocs[j].metadata), + docMetadata: JSON.stringify(embeddingDocs[j].metadata), dataType: embeddingDocs[j].dataType || '', }; @@ -331,15 +334,16 @@ export async function createChromaCollection< chromaEmbedder = { generate(texts: string[]) { return Promise.all( - texts.map( - (text) => - ai.embed({ - embedder, - content: text, - options: params.embedderOptions, - })[0].embedding // Text only has a single embedding + texts.map((text) => + ai.embed({ + embedder, + content: text, + options: params.embedderOptions, + }) ) - ); + ).then((results: Embedding[][]) => { + return results.map((result: Embedding[]) => result[0].embedding); + }); }, }; } diff --git a/js/plugins/evaluators/src/metrics/answer_relevancy.ts b/js/plugins/evaluators/src/metrics/answer_relevancy.ts index 13c669eba..fbf01f7ee 100644 --- a/js/plugins/evaluators/src/metrics/answer_relevancy.ts +++ b/js/plugins/evaluators/src/metrics/answer_relevancy.ts @@ -78,16 +78,20 @@ export async function answerRelevancyScore< if (!genQuestion) throw new Error('Error generating question for answer relevancy'); - const questionEmbed = await ai.embed({ - embedder, - content: input, - options: embedderOptions, - })[0].embedding; // Single embedding for text - const genQuestionEmbed = await ai.embed({ - embedder, - content: genQuestion, - options: embedderOptions, - })[0].embedding; // Single embedding for text + const questionEmbed = ( + await ai.embed({ + embedder, + content: input, + options: embedderOptions, + }) + )[0].embedding; // Single embedding for text + const genQuestionEmbed = ( + await ai.embed({ + embedder, + content: genQuestion, + options: embedderOptions, + }) + )[0].embedding; // Single embedding for text const score = cosineSimilarity(questionEmbed, genQuestionEmbed); const answered = response.output?.answered === '1' ? 1 : 0; const isNonCommittal = response.output?.noncommittal === '1' ? 1 : 0; diff --git a/js/plugins/firebase/src/firestoreRetriever.ts b/js/plugins/firebase/src/firestoreRetriever.ts index 86b0194b7..0169a0211 100644 --- a/js/plugins/firebase/src/firestoreRetriever.ts +++ b/js/plugins/firebase/src/firestoreRetriever.ts @@ -135,8 +135,7 @@ export function defineFirestoreRetriever( query = query.where(field, '==', options.where![field]); } // Single embedding for text input - const embeddings = await ai.embed({ embedder, content }); - const embedding = embeddings[0].embedding; + const embedding = (await ai.embed({ embedder, content }))[0].embedding; const result = await query .findNearest(vectorField, embedding, { limit: options.limit || 10, diff --git a/js/plugins/ollama/tests/embedding_live_test.ts b/js/plugins/ollama/tests/embedding_live_test.ts index 658be5408..73a286944 100644 --- a/js/plugins/ollama/tests/embedding_live_test.ts +++ b/js/plugins/ollama/tests/embedding_live_test.ts @@ -46,10 +46,12 @@ describe('defineOllamaEmbedder - Live Tests', () => { dimensions: 768, options, }); - const result = await ai.embed({ - embedder, - content: 'Hello, world!', - }); + const result = ( + await ai.embed({ + embedder, + content: 'Hello, world!', + }) + )[0].embedding; assert.strictEqual(result.length, 768); }); }); diff --git a/js/plugins/vertexai/src/vectorsearch/vector_search/retrievers.ts b/js/plugins/vertexai/src/vectorsearch/vector_search/retrievers.ts index b5c8ec81e..576c44ff4 100644 --- a/js/plugins/vertexai/src/vectorsearch/vector_search/retrievers.ts +++ b/js/plugins/vertexai/src/vectorsearch/vector_search/retrievers.ts @@ -65,11 +65,13 @@ export function vertexAiRetrievers( ); } - const queryEmbeddings = await ai.embed({ - embedder: embedderReference, - options: embedderOptions, - content, - })[0].embedding; // Single embedding for text + const queryEmbeddings = ( + await ai.embed({ + embedder: embedderReference, + options: embedderOptions, + content, + }) + )[0].embedding; // Single embedding for text const accessToken = await params.authClient.getAccessToken(); diff --git a/js/testapps/multimodal/src/genkit.ts b/js/testapps/multimodal/src/genkit.ts index ca1c52eba..e52c5c854 100644 --- a/js/testapps/multimodal/src/genkit.ts +++ b/js/testapps/multimodal/src/genkit.ts @@ -20,16 +20,57 @@ import { multimodalEmbedding001, vertexAI, } from '@genkit-ai/vertexai'; -import { genkit } from 'genkit'; +import { Genkit, genkit } from 'genkit'; +import { chroma } from 'genkitx-chromadb'; +import { pinecone } from 'genkitx-pinecone'; +import { GoogleAuth, IdTokenClient } from 'google-auth-library'; -export const ai = genkit({ +const auth = new GoogleAuth(); +let authClient: IdTokenClient | undefined = undefined; + +/** Helper method to cache {@link IdTokenClient} instance */ +async function getCloudRunAuthClient(aud: string) { + if (!authClient) { + authClient = await auth.getIdTokenClient(aud); + } + return authClient; +} + +export const ai: Genkit = genkit({ plugins: [ vertexAI({ location: 'us-central1', }), + pinecone([ + { + indexId: 'pinecone-multimodal-index', + embedder: multimodalEmbedding001, + }, + ]), + chroma([ + { + collectionName: 'multimodal_collection', + embedder: multimodalEmbedding001, + createCollectionIfMissing: true, + clientParams: async () => { + // Replace this with your Cloud Run Instance URL + const host = 'https://.run.app'; + const client = await getCloudRunAuthClient(host); + const idToken = await client.idTokenProvider.fetchIdToken(host); + return { + path: host, + fetchOptions: { + headers: { + Authorization: 'Bearer ' + idToken, + }, + }, + }; + }, + }, + ]), devLocalVectorstore([ { - indexName: 'multiModalIndex', + indexName: 'localMultiModalIndex', embedder: multimodalEmbedding001, }, ]), diff --git a/js/testapps/multimodal/src/pdf.ts b/js/testapps/multimodal/src/pdf.ts index c53ea4913..5a257c7b9 100644 --- a/js/testapps/multimodal/src/pdf.ts +++ b/js/testapps/multimodal/src/pdf.ts @@ -18,7 +18,6 @@ import { devLocalIndexerRef, devLocalRetrieverRef, } from '@genkit-ai/dev-local-vectorstore'; -import { startFlowServer } from '@genkit-ai/express'; import fileTypeChecker from 'file-type-checker'; import fs from 'fs'; import { Document, z } from 'genkit'; @@ -176,7 +175,3 @@ async function extractText(filePath: string) { const data = await pdf(dataBuffer); return data.text; } - -startFlowServer({ - flows: [indexMultimodalPdf, multimodalPdfQAFlow], -}); diff --git a/js/testapps/multimodal/src/video.ts b/js/testapps/multimodal/src/video.ts index 9902e9c8e..8d94c2870 100644 --- a/js/testapps/multimodal/src/video.ts +++ b/js/testapps/multimodal/src/video.ts @@ -21,18 +21,43 @@ import { import fileTypeChecker from 'file-type-checker'; import fs from 'fs'; import { Document, z } from 'genkit'; +import { chromaIndexerRef, chromaRetrieverRef } from 'genkitx-chromadb'; +import { pineconeIndexerRef, pineconeRetrieverRef } from 'genkitx-pinecone'; import path from 'path'; + import { ai } from './genkit.js'; import { augmentedVideoPrompt } from './prompt.js'; -// Simple aliases for readability -export const videoRetriever = devLocalRetrieverRef('multiModalIndex'); -export const videoIndexer = devLocalIndexerRef('multiModalIndex'); +export const localVideoRetriever = devLocalRetrieverRef('localMultiModalIndex'); +export const localVideoIndexer = devLocalIndexerRef('localMultiModalIndex'); + +// Before using this, set up a pinecone database with +// dimension: 1408 and metric: cosine. +// Also set the PINECONE_API_KEY environment variable with your key. +export const pineconeVideoRetriever = pineconeRetrieverRef({ + indexId: 'pinecone-multimodal-index', + displayName: 'Pinecone video retriever', +}); + +export const pineconeVideoIndexer = pineconeIndexerRef({ + indexId: 'pinecone-multimodal-index', + displayName: 'Pinecone video indexer', +}); + +export const chromaVideoRetriever = chromaRetrieverRef({ + collectionName: 'multimodal_collection', + displayName: 'Chroma Video retriever', +}); -// Define a video indexer flow -export const indexVideo = ai.defineFlow( +export const chromaVideoIndexer = chromaIndexerRef({ + collectionName: 'multimodal_collection', + displayName: 'Chroma video indexer', +}); + +// Define a local video indexer flow +export const localIndexVideo = ai.defineFlow( { - name: 'indexVideo', + name: 'localIndexVideo', inputSchema: z .string() .describe( @@ -45,7 +70,51 @@ export const indexVideo = ai.defineFlow( ); await ai.index({ - indexer: videoIndexer, + indexer: localVideoIndexer, + documents, + }); + } +); + +// Define a pinecone video indexer flow +export const pineconeIndexVideo = ai.defineFlow( + { + name: 'pineconeIndexVideo', + inputSchema: z + .string() + .describe( + `Video URL. e.g. 'gs://cloud-samples-data/generative-ai/video/pixel8.mp4'` + ), + }, + async (videoUrl: string) => { + const documents = await ai.run('extract-video', () => + extractVideo(videoUrl) + ); + + await ai.index({ + indexer: pineconeVideoIndexer, + documents, + }); + } +); + +// Define a chroma video indexer flow +export const chromaIndexVideo = ai.defineFlow( + { + name: 'chromaIndexVideo', + inputSchema: z + .string() + .describe( + `Video URL. e.g. 'gs://cloud-samples-data/generative-ai/video/pixel8.mp4'` + ), + }, + async (videoUrl: string) => { + const documents = await ai.run('extract-video', () => + extractVideo(videoUrl) + ); + + await ai.index({ + indexer: chromaVideoIndexer, documents, }); } @@ -132,15 +201,96 @@ async function extractVideo(filePath: string): Promise { } // Define a video QA flow -export const VideoQAFlow = ai.defineFlow( +export const localVideoQAFlow = ai.defineFlow( + { + name: 'localVideoQuestions', + inputSchema: z.string(), + outputSchema: z.string(), + }, + async (query: string, { sendChunk }) => { + const docs = (await ai.retrieve({ + retriever: localVideoRetriever, + query, + options: { k: 1 }, // we are choosing a single segment of video for context + })) as Document[]; + + return augmentedVideoPrompt( + { + question: query, + media: docs + .filter( + (d) => d.media[0]?.url?.length && d.media[0]?.contentType?.length + ) + .map((d) => { + console.log( + `Retriever returned video: ${d.media[0].url} from ${d.metadata?.embedMetadata?.startOffsetSec}s to ${d.metadata?.embedMetadata?.endOffsetSec}s` + ); + return { + gcsUrl: d.media[0]?.url, + contentType: d.media[0]?.contentType || '', + startOffsetSec: d.metadata?.embedMetadata + ?.startOffsetSec as number, + endOffsetSec: d.metadata?.embedMetadata?.endOffsetSec as number, + }; + })[0], + }, + { + onChunk: (c) => sendChunk(c.text), + } + ).then((r) => r.text); + } +); + +// Define a video QA flow +export const pineconeVideoQAFlow = ai.defineFlow( + { + name: 'pineconeVideoQuestions', + inputSchema: z.string(), + outputSchema: z.string(), + }, + async (query: string, { sendChunk }) => { + const docs = (await ai.retrieve({ + retriever: pineconeVideoRetriever, + query, + options: { k: 1 }, // we are choosing a single segment of video for context + })) as Document[]; + + return augmentedVideoPrompt( + { + question: query, + media: docs + .filter( + (d) => d.media[0]?.url?.length && d.media[0]?.contentType?.length + ) + .map((d) => { + console.log( + `Retriever returned video: ${d.media[0].url} from ${d.metadata?.embedMetadata?.startOffsetSec}s to ${d.metadata?.embedMetadata?.endOffsetSec}s` + ); + return { + gcsUrl: d.media[0]?.url, + contentType: d.media[0]?.contentType || '', + startOffsetSec: d.metadata?.embedMetadata + ?.startOffsetSec as number, + endOffsetSec: d.metadata?.embedMetadata?.endOffsetSec as number, + }; + })[0], + }, + { + onChunk: (c) => sendChunk(c.text), + } + ).then((r) => r.text); + } +); + +export const chromaVideoQAFlow = ai.defineFlow( { - name: 'videoQuestions', + name: 'chromaVideoQuestions', inputSchema: z.string(), outputSchema: z.string(), }, - async (query, { sendChunk }) => { + async (query: string, { sendChunk }) => { const docs = (await ai.retrieve({ - retriever: videoRetriever, + retriever: chromaVideoRetriever, query, options: { k: 1 }, // we are choosing a single segment of video for context })) as Document[]; From 076a28aa7e8a834dec70f2fe42c53996f49ed0d0 Mon Sep 17 00:00:00 2001 From: Thomas Bouldin Date: Tue, 4 Feb 2025 10:52:38 -0800 Subject: [PATCH 531/562] BREAKING CHANGE!(js): Rip out onFlow and firebaseAuth. Update all docs (#1798) onFlow is being removed in favor of onCallGenkit in firebase-functions/https. This allows users to use genkit consistently across all environments. Web frameworks (e.g. express, next, firebase-functions) use simple handler functions to use a flow that is defined abstract of the transport protocol. firebaseAuth is being temporarily removed because it is tightly coupled with onFlow. It will be added in the future as generic middleware that can be used by @genkit-ai/express and @genkit-ai/next to both allow easy enforcement of auth/appCheck and to provide a contex to the flow that is consistent with the one provided by onCallGenkit. Co-authored-by: Daniel Lee Co-authored-by: Michael Doyle --- docs/auth.md | 193 +++-- docs/firebase.md | 182 ++--- docs/flows.md | 5 +- docs/plugins/firebase.md | 80 +- js/doc-snippets/package.json | 12 +- js/doc-snippets/src/flows/firebase.ts | 23 +- js/plugins/firebase/package.json | 29 +- js/plugins/firebase/src/auth.ts | 86 --- js/plugins/firebase/src/functions.ts | 175 ----- js/plugins/firebase/src/helpers.ts | 115 --- js/plugins/firebase/tests/auth_test.ts | 54 -- js/plugins/firebase/tests/functions_test.ts | 176 ----- js/pnpm-lock.yaml | 718 +++--------------- js/testapps/dev-ui-gallery/package.json | 3 +- .../src/main/flows-firebase-functions.ts | 66 +- js/testapps/esm/src/index.ts | 2 - .../functions/package.json | 10 +- .../functions/src/index.ts | 133 ++-- 18 files changed, 394 insertions(+), 1668 deletions(-) delete mode 100644 js/plugins/firebase/src/auth.ts delete mode 100644 js/plugins/firebase/src/functions.ts delete mode 100644 js/plugins/firebase/tests/auth_test.ts delete mode 100644 js/plugins/firebase/tests/functions_test.ts diff --git a/docs/auth.md b/docs/auth.md index aedeb4f8d..5ba3b4ebf 100644 --- a/docs/auth.md +++ b/docs/auth.md @@ -10,53 +10,51 @@ are properly scoped to the user invoking the LLM, and the flow is being invoked only by verified client applications. Firebase Genkit provides mechanisms for managing authorization policies and -contexts. For flows running on Cloud Functions for Firebase, developers are -required to provide an auth policy or else explicitly acknowledge the lack of -one. For non-Functions flows, auth can be managed and set as well, but requires -a bit more manual integration. +contexts. Flows running on Firebase can use an auth policy callback (or helper) +or Firebase will provide auth context into the flow where it can do its own checks. +For non-Functions flows, auth can be managed and set as well through middleware. ## Basic flow authorization -All flows can define an `authPolicy` in their config. An auth policy is a function that tests if certain criteria (defined by you) are met, and throws an exception if any test fails. -If this field is set, it is executed before the flow is invoked: +Flows can check authorization in two ways: either the request binding (e.g. `onCallGenkit` for +Cloud Functions for Fireabse or `express`) can enforce authorization, or those frameworks +can pass auth policies to the flow itself where the flow will have access to the information +for auth managed within the flow. ```ts import { genkit, z } from 'genkit'; const ai = genkit({ ... }); -export const selfSummaryFlow = ai.defineFlow( - { - name: 'selfSummaryFlow', - inputSchema: z.object({ uid: z.string() }), - outputSchema: z.string(), - authPolicy: (auth, input) => { - if (!auth) { - throw new Error('Authorization required.'); - } - if (input.uid !== auth.uid) { - throw new Error('You may only summarize your own profile data.'); - } - }, - }, - async (input) => { - // Flow logic here... +export const selfSummaryFlow = ai.defineFlow( { + name: 'selfSummaryFlow', + inputSchema: z.object({ uid: z.string() }), + outputSchema: z.string(), +}, async (input, { context }) => { + if (!context.auth) { + throw new Error('Authorization required.'); } -); + if (input.uid !== context.auth.uid) { + throw new Error('You may only summarize your own profile data.'); + } + // Flow logic here... +}); ``` -When executing this flow, you _must_ provide an auth object using `withLocalAuthContext` or else you'll -receive an error: +It is up to the request binding to populate `context.auth` in this case. For example, `onCallGenkit` +will automatically populate `context.auth` (Firebase Authentication), `context.app` (Firebase App Check), +and `context.instanceIdToken` (Firebase Cloud Messaging). When calling a flow manually, you can add +your own auth context manually. ```ts // Error: Authorization required. await selfSummaryFlow({ uid: 'abc-def' }); // Error: You may only summarize your own profile data. -await selfSummaryFlow( +await selfSummaryFlow.run( { uid: 'abc-def' }, { - withLocalAuthContext: { uid: 'hij-klm' }, + context: { auth: { uid: 'hij-klm' } }, } ); @@ -64,7 +62,7 @@ await selfSummaryFlow( await selfSummaryFlow( { uid: 'abc-def' }, { - withLocalAuthContext: { uid: 'abc-def' }, + context: { auth: { uid: 'abc-def' } }, } ); ``` @@ -82,7 +80,9 @@ const ai = genkit({ ... });; async function readDatabase(uid: string) { const auth = ai.currentContext()?.auth; - if (auth?.admin) { + // Note: the shape of context.auth depends on the provider. onCallGenkit puts + // claims information in auth.token + if (auth?.token?.admin) { // Do something special if the user is an admin } else { // Otherwise, use the `uid` variable to retrieve the relevant document @@ -111,47 +111,42 @@ genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --auth '{"uid": "abc-def"}' ## Cloud Functions for Firebase integration -The Firebase plugin provides convenient integration with Firebase Auth / Google +The Cloud Functions for Firebase SDK Firebase supports Genkit including integration with Firebase Auth / Google Cloud Identity Platform as well as built-in Firebase App Check support. ### Authorization -The `onFlow()` wrapper provided by the Firebase plugin works natively with the +The `onCallGenkit()` wrapper provided by the Firebase Functions library works natively with the Cloud Functions for Firebase [client SDKs](https://firebase.google.com/docs/functions/callable?gen=2nd#call_the_function). When using the SDK, the Firebase Auth header will automatically be included as long as your app client is also using the [Firebase Auth SDK](https://firebase.google.com/docs/auth). -You can use Firebase Auth to protect your flows defined with `onFlow()`: +You can use Firebase Auth to protect your flows defined with `onCallGenkit()`: ```ts import { genkit } from 'genkit'; -import { firebaseAuth } from '@genkit-ai/firebase'; -import { onFlow } from '@genkit-ai/firebase/functions'; +import { onCallGenkit } from 'firebase-functions/https'; const ai = genkit({ ... });; -export const selfSummaryFlow = onFlow( - ai, - { - name: 'selfSummaryFlow', - inputSchema: z.string(), - outputSchema: z.string(), - authPolicy: firebaseAuth((user) => { - if (!user.email_verified && !user.admin) { - throw new Error('Email not verified'); - } - }), - }, - async (input) => { - // Flow logic here... - } -); +const selfSummaryFlow = ai.defineFlow({ + name: 'selfSummaryFlow', + inputSchema: z.string(), + outputSchema: z.string(), +}, async (input) => { + // Flow logic here... +}); + +export const selfSummary = onCallGenkit({ + authPolicy: (auth) => auth?.token?.['email_verified'] && auth?.token?.['admin'], +}, selfSummaryFlow); ``` -When using the Firebase Auth plugin, `user` will be returned as a +When using the `onCallGenkit`, `context.auth` will be returned as an object with +a `uid` for the user ID, and a `token` that is a [DecodedIdToken](https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.decodedidtoken). You can always retrieve this object at any time via `ai.currentContext()` as noted above. When running this flow during development, you would pass the user object @@ -161,50 +156,34 @@ in the same way: genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --auth '{"admin": true}' ``` -By default the Firebase Auth plugin requires the auth header to be sent by the -client, but in cases where you wish to allow unauthenticated access with special -handling for authenticated users (upselling features, say), then you can -configure the policy like so: - - - -```ts -authPolicy: firebaseAuth((user) => { - if (user && !user.email_verified) { - throw new Error("Logged in users must have verified emails"); - } -}, {required: false}), -``` - Whenever you expose a Cloud Function to the wider internet, it is vitally important that you use some sort of authorization mechanism to protect your data and the data of your customers. With that said, there are times when you need to deploy a Cloud Function with no code-based authorization checks (for example, your Function is not world-callable but instead is protected by -[Cloud IAM](https://cloud.google.com/functions/docs/concepts/iam)). The -`authPolicy` field is always required when using `onFlow()`, but you can -indicate to the library that you are forgoing authorization checks by using the -`noAuth()` function: +[Cloud IAM](https://cloud.google.com/functions/docs/concepts/iam)). Cloud Functions +for Firebase allows you to do this using the `invoker` property, which controls +IAM access. The special value `'private'` leaves the function as the default IAM +setting, which means that only callers with the [Cloud Run Invoker role](https://cloud.google.com/run/docs/reference/iam/roles) +can execute the function. You can instead provide the email address of a user +or service account that should be granted permission to call this exact function. ```ts -import { onFlow, noAuth } from "@genkit-ai/firebase/functions"; - -export const selfSummaryFlow = onFlow( - ai, - { - name: "selfSummaryFlow", - inputSchema: z.string(), - outputSchema: z.string(), - // WARNING: Only do this if you have some other gatekeeping in place, like - // Cloud IAM! - authPolicy: noAuth(), - }, - async (input) => { - // Flow logic here... - } -); +import { onCallGenkit } from 'firebase-functions/https' + +const selfSummaryFlow = ai.defineFlow({ + name: 'selfSummaryFlow', + inputSchema: z.string(), + outputSchema: z.string(), +}, async (input) => { + // Flow logic here... +}); + +export const selfSummary = onCallGenkit({ + invoker: 'private', +}, selfSummaryFlow); ``` ### Client integrity @@ -213,32 +192,30 @@ Authentication on its own goes a long way to protect your app. But it's also important to ensure that only your client apps are calling your functions. The Firebase plugin for genkit includes first-class support for [Firebase App Check](https://firebase.google.com/docs/app-check). Simply add -the following configuration options to your `onFlow()`: +the following configuration options to your `onCallGenkit()`: ```ts -import { onFlow } from "@genkit-ai/firebase/functions"; - -export const selfSummaryFlow = onFlow( - ai, - { - name: "selfSummaryFlow", - inputSchema: z.string(), - outputSchema: z.string(), - - // These two fields for app check. The consumeAppCheckToken option is for - // replay protection, and requires additional client configuration. See the - // App Check docs. - enforceAppCheck: true, - consumeAppCheckToken: true, - - authPolicy: ..., - }, - async (input) => { - // Flow logic here... - } -); +import { onCallGenkit } from 'firebase-functions/https'; + +const selfSummaryFlow = ai.defineFlow({ + name: 'selfSummaryFlow', + inputSchema: z.string(), + outputSchema: z.string(), +}, async (input) => { + // Flow logic here... +}); + +export const selfSummary = onCallGenkit({ + // These two fields for app check. The consumeAppCheckToken option is for + // replay protection, and requires additional client configuration. See the + // App Check docs. + enforceAppCheck: true, + consumeAppCheckToken: true, + + authPolicy: ..., +}, selfSummaryFlow); ``` ## Non-Firebase HTTP authorization diff --git a/docs/firebase.md b/docs/firebase.md index 4f81324b0..16ade8e55 100644 --- a/docs/firebase.md +++ b/docs/firebase.md @@ -1,8 +1,10 @@ # Deploy flows using Cloud Functions for Firebase -Genkit includes a plugin that helps you deploy your flows to Cloud Functions for -Firebase. Flows, once deployed, are available as HTTPS endpoints and accessible -as callable functions using the Cloud Functions client libraries. +Cloud Functions for Firebase has an `onCallGenkit` method that allows you to +quickly create a [callable function](https://firebase.google.com/docs/functions/callable?gen=2nd) +with a Genkit action (e.g. a Flow). These functions can be called with `genkit/beta/client` or the +[Functions client SDK](https://firebase.google.com/docs/functions/callable?gen=2nd#call_the_function), +which will automatically add auth info. ## Before you begin @@ -55,16 +57,15 @@ up, follow these steps: in TypeScript, but you can also deploy your Genkit flows if you're using JavaScript. -## 2. Update flow definitions +## 2. Wrap the Flow in onCallGenkit After you've set up a Firebase project with Cloud Functions, you can copy or write flow definitions in the project’s `functions/src` directory, and export them in `index.ts`. -For your flows to be deployable, you will need to make some small changes to how -you define them. The core logic will remain the same, but you will add some -additional information to make them smoothly deployable and more secure once -deployed. +For your flows to be deployable, you will need to wrap them in `onCallGenkit`. +This method has all the features of the normal `onCall`. It automatically supports +both streaming and JSON responses. Suppose you have the following flow: @@ -82,67 +83,38 @@ const generatePoemFlow = ai.defineFlow( ); ``` -The following sections describe the changes you need to make before you can -deploy it. - -### Define flows with onFlow - -Instead of defining your flow with `Genkit.defineFlow()`, use the Firebase -plugin's `onFlow()` function. Using this function wraps your flow logic in a -Cloud Functions request handler, similar to -[`onCall`](/docs/functions/callable?gen=2nd#write_and_deploy_the_callable_function). +You can expose this Flow as a Callable Function using `onCallGenkit`: ```ts -import { onFlow } from "@genkit-ai/firebase/functions"; +import { onCallGenkit } from 'firebase-functions/https'; -export const generatePoem = onFlow( - ai, - { - // ... - }, - async (subject: string) => { - // ... - } -); +export generatePoem = onCallGenkit(generatePoemFlow); ``` -Note that `onFlow` isn't a method of `Genkit`, but rather a function that takes -a `Genkit` instance as its first parameter. Otherwise, the syntax is similar to -`defineFlow`. - ### Define an authorization policy All deployed flows, whether deployed to Firebase or not, should have an authorization policy; without one, your potentially-expensive generative AI flows would be invocable by anyone. To define an authorization policy, use the -`authPolicy` parameter in the flow definition: +`authPolicy` parameter in `onCallGenkit`: ```ts -import { firebaseAuth } from "@genkit-ai/firebase/auth"; - -export const generatePoem = onFlow( - ai, - { - name: "generatePoem", - // ... - authPolicy: firebaseAuth((user, input) => { - if (!user.email_verified) { - throw new Error("Verified email required to run flow"); - } - }), - }, - async (subject: string) => { - // ... - } -); +export const generatePoem = onCallGenkit({ + authPolicy: (auth) => auth?.token?.email_verified, +}, generatePoemFlow); ``` -This policy uses the `firebaseAuth()` helper to allow access only to registered -users of your app with verfied email addresses. On the client side, you need to -set the `Authorization: Bearer` header to a Firebase ID token that satisfies -your policy. The Cloud Functions client SDKs provide callable function methods -that automate this; see the section [Try the deployed flow](#example-client) for -an example. +This sample uses a manual function as its auth policy. In addition, the https +library exports the helpers `signedIn()` and `hasClaim()`. Here is the same code +using one of those helpers: + +```ts +import { hasClaim } from 'firebase-functions/https'; + +export const generatePoem = onCallGenkit({ + authPolicy: hasClaim('email_verified'), +}, generatePoemFlow); +``` ### Make API credentials available to deployed flows @@ -184,18 +156,9 @@ chose: to this secret value: ```ts - export const generatePoem = onFlow( - { - name: "generatePoem", - // ... - httpsOptions: { - secrets: [googleAIapiKey], // Add this line. - }, - }, - async (subject) => { - // ... - } - ); + export const generatePoem = onCallGenkit({ + secrets: [googleAIapiKey] + }, generatePoemFlow); ``` Now, when you deploy this function, your API key will be stored in Cloud @@ -214,30 +177,32 @@ chose: The only secret you need to set up for this tutorial is for the model provider, but in general, you must do something similar for each service your flow uses. +### Add App Check enforcement +[Firebase App Check](https://firebase.google.com/docs/app-check) uses native attestation +to verify that your API is only being called by your application. onCallGenkit supports App Check enforcement declaratively. + +```ts +export const generatePoem = onCallGenkit({ + enforceAppCheck: true, + // Optional. Makes App Check tokens only usable once. This adds extra security + // at the expense of slowing down your app to generate a token for every API + // call + consumeAppCheckToken: true, +}, generatePoemFlow); +``` + ### Set a CORS policy -If you'll access your flow from a web app (which you will do in the [Try the -deployed flow](#example-client) section), in the `httpsOptions` parameter, set a -CORS policy: +Callable functions default to allowing any domain to call your function. If you would +like to customize this, use the `cors` option. ```ts -export const generatePoem = onFlow( - ai, - { - name: "generatePoem", - // ... - httpsOptions: { - cors: '*', - }, - }, - async (subject: string) => { - // ... - } -); +export const generatePoem = onCallGenkit({ + cors: 'mydomain.com', +}, generatePoemFlow); ``` -You will likely want a more restrictive policy for production apps, but this -will do for this tutorial. +With proper authentication (especially App Check), CORS is often unnecessary. ### Complete example @@ -245,34 +210,31 @@ After you've made all of the changes described above, your deployable flow will look something like the following example: ```ts -const googleAIapiKey = defineSecret("GOOGLE_GENAI_API_KEY"); - -export const generatePoem = onFlow( - ai, - { - name: "generatePoem", - inputSchema: z.string(), - outputSchema: z.string(), - authPolicy: firebaseAuth((user, input) => { - if (!user.email_verified) { - throw new Error("Verified email required to run flow"); - } - }), - httpsOptions: { - secrets: [googleAIapiKey], - cors: '*', - }, - }, - async (subject: string) => { - const { text } = await ai.generate(`Compose a poem about ${subject}.`); - return text; - } -); +import { genkit } from 'genkit'; +import { onCallGenkit, hasClaim } from 'firebase-functions/https'; +import { defineSecret } from 'firebase-functions/params'; + +const apiKey = defineSecret("GOOGLE_GENAI_API_KEY"); + +const generatePoemFlow = ai.defineFlow({ + name: "generatePoem", + inputSchema: z.string(), + outputSchema: z.string(), +}, async (subject: string) => { + const { text } = await ai.generate(`Compose a poem about ${subject}.`); + return text; +}); + +export const generateFlow = onCallGenkit({ + secrets: [apiKey], + authPolicy: hasClaim("email_verified"), + enforceAppCheck: true, +}, generatePoemFlow); ``` ## 3. Deploy flows to Firebase -After you've defined flows using `onFlow`, you can deploy them as you would +After you've defined flows using `onCallGenkit`, you can deploy them as you would deploy other Cloud Functions: ```posix-terminal @@ -391,7 +353,7 @@ endpoint requests. ## Optional: Run flows in the developer UI -You can run flows defined using `onFlow` in the developer UI, exactly the same +You can run flows defined using `onCallGenkit` in the developer UI, exactly the same way as you run flows defined using `defineFlow`, so there's no need to switch between the two between deployment and development. diff --git a/docs/flows.md b/docs/flows.md index 2a88b22c2..7eb7b71f3 100644 --- a/docs/flows.md +++ b/docs/flows.md @@ -240,9 +240,8 @@ but this section gives brief overviews of your deployment options. ### Cloud Functions for Firebase -To deploy flows with Cloud Functions for Firebase, use the `firebase` plugin. In -your flow definitions, replace `defineFlow` with `onFlow` and include an -`authPolicy`. +To deploy flows with Cloud Functions for Firebase, use the `onCallGenkit` feature of `firebase-functions/https`. +This will wrap your flow in a callable function. You may set an auth policy and configure App Check. ```ts {% includecode github_path="firebase/genkit/js/doc-snippets/src/flows/firebase.ts" region_tag="ex" adjust_indentation="auto" %} diff --git a/docs/plugins/firebase.md b/docs/plugins/firebase.md index a173e115a..e1d6518d1 100644 --- a/docs/plugins/firebase.md +++ b/docs/plugins/firebase.md @@ -5,9 +5,13 @@ use https://github.com/firebase/firebase-tools/blob/master/templates/init/functi The Firebase plugin provides integrations with Firebase services, allowing you to build intelligent and scalable AI applications. Key features include: +<<<<<<< HEAD +- **Firestore Vector Store**: Use Firestore for indexing and retrieval with vector embeddings. +======= - **Firestore Vector Store**: Use Firestore for indexing and retrieval with vector embeddings. - **Cloud Functions**: Deploy flows as HTTPS-triggered functions. - **Firebase Authentication**: Implement authorization policies. +>>>>>>> origin/main - **Telemetry**: Export telemetry to [Google's Cloud operations suite](https://cloud.google.com/products/operations) that powers the Firebase Genkit Monitoring console. ## Installation @@ -265,25 +269,28 @@ The prior example requires the `embedding` field to be indexed to work. To creat ### Deploy flows as Cloud Functions -The plugin provides the `onFlow()` constructor, which creates a flow backed by a Cloud Functions for Firebase HTTPS-triggered function. These functions conform to Firebase's [callable function interface](https://firebase.google.com/docs/functions/callable-reference) and you can use the [Cloud Functions client SDKs](https://firebase.google.com/docs/functions/callable?gen=2nd#call_the_function) to call them. +To deploy a flow with Cloud Functions, use the Firebase Functions library's native support for genkit. The `onCallGenkit` method allows +you to create a [callable function](https://firebase.google.com/docs/functions/callable?gen=2nd) from a flow. It will automatically support +streaming and JSON requests. You can use the [Cloud Functions client SDKs](https://firebase.google.com/docs/functions/callable?gen=2nd#call_the_function) to call them. ```js -import { onFlow, noAuth } from "@genkit-ai/firebase/functions"; +import { onCallGenkit } from 'firebase-functions/https'; +import { defineSecret } from 'firebase-functions/params'; -export const exampleFlow = onFlow( - ai, // Provide the Genkit instance - { - name: "exampleFlow", - authPolicy: noAuth(), // WARNING: noAuth() creates an open endpoint! - }, - async (prompt) => { +export const exampleFlow = ai.defineFlow({ + name: "exampleFlow", +}, async (prompt) => { // Flow logic goes here. return response; } ); + +// WARNING: This has no authentication or app check protections. +// See github.com/firebase/genkit/blob/main/docs/auth.md for more information. +export const example = onCallGenkit({ secrets: [apiKey] }, exampleFlow); ``` Deploy your flow using the Firebase CLI: @@ -291,58 +298,3 @@ Deploy your flow using the Firebase CLI: ``` firebase deploy --only functions ``` - -The `onFlow()` function has some options not present in `defineFlow()`: - -- `httpsOptions`: an [`HttpsOptions`](https://firebase.google.com/docs/reference/functions/2nd-gen/node/firebase-functions.https.httpsoptions) object used to configure your Cloud Function: - - - - ```js - export const exampleFlow = onFlow( - ai, - { - name: "exampleFlow", - httpsOptions: { - cors: true, - }, - // ... - }, - async (prompt) => { - // ... - } - ); - ``` - -- `enforceAppCheck`: when `true`, reject requests with missing or invalid [App Check](https://firebase.google.com/docs/app-check) tokens. - -- `consumeAppCheckToken`: when `true`, invalidate the App Check token after verifying it. - - See [Replay protection](https://firebase.google.com/docs/app-check/cloud-functions#replay-protection). - -### Firebase Authentication - -This plugin provides a helper function to create authorization policies around Firebase Auth: - - - -```js -import {firebaseAuth} from "@genkit-ai/firebase/auth"; - -export const exampleFlow = onFlow( - ai, - { - name: "exampleFlow", - authPolicy: firebaseAuth((user) => { - if (!user.email_verified) throw new Error("Requires verification!"); - }), - }, - async (prompt) => { - // ... - } -); -``` - -To define an auth policy, provide `firebaseAuth()` with a callback function that takes a [`DecodedIdToken`](https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.decodedidtoken) as its only parameter. In this function, examine the user token and throw an error if the user fails to meet any of the criteria you want to require. - -See [Authorization and integrity](http://../auth.md) for a more thorough discussion of this topic. diff --git a/js/doc-snippets/package.json b/js/doc-snippets/package.json index 37e54440f..66574a643 100644 --- a/js/doc-snippets/package.json +++ b/js/doc-snippets/package.json @@ -12,17 +12,17 @@ "build:watch": "tsc --watch" }, "dependencies": { - "genkit": "workspace:*", + "@genkit-ai/express": "workspace:*", "@genkit-ai/googleai": "workspace:*", "@genkit-ai/vertexai": "workspace:*", - "@genkit-ai/firebase": "workspace:*", - "@genkit-ai/express": "workspace:*", - "data-urls": "^5.0.0" + "data-urls": "^5.0.0", + "firebase-functions": "^6.3.1", + "genkit": "workspace:*" }, "devDependencies": { + "@types/data-urls": "^3.0.4", "npm-run-all": "^4.1.5", "rimraf": "^6.0.1", - "typescript": "^5.6.3", - "@types/data-urls": "^3.0.4" + "typescript": "^5.6.3" } } diff --git a/js/doc-snippets/src/flows/firebase.ts b/js/doc-snippets/src/flows/firebase.ts index 015ee6137..b96dbf16e 100644 --- a/js/doc-snippets/src/flows/firebase.ts +++ b/js/doc-snippets/src/flows/firebase.ts @@ -18,21 +18,26 @@ import { genkit } from 'genkit'; const ai = genkit({}); // [START ex] -import { firebaseAuth } from '@genkit-ai/firebase/auth'; -import { onFlow } from '@genkit-ai/firebase/functions'; +import { hasClaim, onCallGenkit } from 'firebase-functions/https'; +import { defineSecret } from 'firebase-functions/params'; -export const menuSuggestion = onFlow( - ai, +const apiKey = defineSecret('GOOGLE_AI_API_KEY'); + +const menuSuggestionFlow = ai.defineFlow( { name: 'menuSuggestionFlow', - authPolicy: firebaseAuth((user) => { - if (!user.email_verified) { - throw new Error('Verified email required to run flow'); - } - }), }, async (restaurantTheme) => { // ... } ); + +export const menuSuggestion = onCallGenkit( + { + secrets: [apiKey], + authPolicy: hasClaim('email_verified'), + }, + menuSuggestionFlow +); + // [END ex] diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index 4edc1fde1..b545a1438 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -31,15 +31,11 @@ "author": "genkit", "license": "Apache-2.0", "dependencies": { - "@genkit-ai/google-cloud": "workspace:^", - "@genkit-ai/express": "workspace:^", - "express": "^4.21.0", - "google-auth-library": "^9.6.3" + "@genkit-ai/google-cloud": "workspace:^" }, "peerDependencies": { "@google-cloud/firestore": "^7.6.0", "firebase-admin": ">=12.2", - "firebase-functions": ">=4.8", "genkit": "workspace:^" }, "devDependencies": { @@ -52,10 +48,7 @@ "@types/jest": "^29.5.12", "@jest/globals": "^29.7.0", "jest": "^29.7.0", - "ts-jest": "^29.1.2", - "express": "^4.21.1", - "get-port": "^5.1.0", - "firebase": "^11.1.0" + "ts-jest": "^29.1.2" }, "types": "./lib/index.d.ts", "exports": { @@ -65,18 +58,6 @@ "types": "./lib/index.d.ts", "default": "./lib/index.js" }, - "./functions": { - "require": "./lib/functions.js", - "import": "./lib/functions.mjs", - "types": "./lib/functions.d.ts", - "default": "./lib/functions.js" - }, - "./auth": { - "require": "./lib/auth.js", - "import": "./lib/auth.mjs", - "types": "./lib/auth.d.ts", - "default": "./lib/auth.js" - }, "./user_engagement": { "require": "./lib/user_engagement.js", "import": "./lib/user_engagement.mjs", @@ -86,12 +67,6 @@ }, "typesVersions": { "*": { - "functions": [ - "lib/functions" - ], - "auth": [ - "lib/auth" - ], "user_engagement": [ "lib/user_engagement" ] diff --git a/js/plugins/firebase/src/auth.ts b/js/plugins/firebase/src/auth.ts deleted file mode 100644 index e9096e2a1..000000000 --- a/js/plugins/firebase/src/auth.ts +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright 2024 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 { RequestWithAuth } from '@genkit-ai/express'; -import { Response } from 'express'; -import { DecodedIdToken, getAuth } from 'firebase-admin/auth'; -import { z } from 'genkit'; -import { FunctionFlowAuth } from './functions.js'; -import { initializeAppIfNecessary } from './helpers.js'; - -export function firebaseAuth( - policy: (user: DecodedIdToken, input: z.infer) => void | Promise -): FunctionFlowAuth; -export function firebaseAuth( - policy: (user: DecodedIdToken, input: z.infer) => void | Promise, - config: { required: true } -): FunctionFlowAuth; -export function firebaseAuth( - policy: ( - user: DecodedIdToken | undefined, - input: z.infer - ) => void | Promise, - config: { required: false } -): FunctionFlowAuth; -export function firebaseAuth( - policy: (user: DecodedIdToken, input: z.infer) => void | Promise, - config?: { required: boolean } -): FunctionFlowAuth { - initializeAppIfNecessary(); - const required = config?.required ?? true; - return { - async policy(auth: unknown | undefined, input: z.infer) { - // If required is true, then auth will always be set when called from - // an HTTP context. However, we need to wrap the user-provided policy - // to check for presence of auth when the flow is executed from runFlow - // or an action context. - if (required && !auth) { - throw new Error('Auth is required'); - } - - return policy(auth as DecodedIdToken, input); - }, - async provider(req, res, next) { - const token = req.headers['authorization']?.split(/[Bb]earer /)[1]; - let decoded: DecodedIdToken; - - if (!token) { - if (required) { - unauthorized(res); - } else { - next(); - } - return; - } - try { - decoded = await getAuth().verifyIdToken(token); - } catch (e) { - unauthorized(res); - return; - } - - (req as RequestWithAuth).auth = decoded; - - next(); - }, - }; -} - -function unauthorized(res: Response) { - res.status(403); - res.send('Unauthorized'); - res.end(); -} diff --git a/js/plugins/firebase/src/functions.ts b/js/plugins/firebase/src/functions.ts deleted file mode 100644 index c60946a55..000000000 --- a/js/plugins/firebase/src/functions.ts +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Copyright 2024 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 { expressHandler } from '@genkit-ai/express'; -import * as express from 'express'; -import { getAppCheck } from 'firebase-admin/app-check'; -import { - HttpsFunction, - HttpsOptions, - onRequest, -} from 'firebase-functions/v2/https'; -import { Flow, FlowFn, Genkit, z } from 'genkit'; -import { logger } from 'genkit/logging'; -import { initializeAppIfNecessary } from './helpers.js'; - -/** - * Flow Auth policy. Consumes the authorization context of the flow and - * performs checks before the flow runs. If this throws, the flow will not - * be executed. - */ -export interface FlowAuthPolicy { - (auth: any | undefined, input: z.infer): void | Promise; -} - -/** - * For express-based flows, req.auth should contain the value to bepassed into - * the flow context. - * - * @hidden - */ -export interface RequestWithAuth extends express.Request { - auth?: unknown; -} - -export type FunctionFlow< - I extends z.ZodTypeAny, - O extends z.ZodTypeAny, - S extends z.ZodTypeAny, -> = HttpsFunction & { flow: Flow }; - -export interface FunctionFlowAuth { - provider: express.RequestHandler; - policy: FlowAuthPolicy; -} - -interface FunctionFlowConfig< - I extends z.ZodTypeAny, - O extends z.ZodTypeAny, - S extends z.ZodTypeAny, -> { - name: string; - inputSchema?: I; - outputSchema?: O; - authPolicy: FunctionFlowAuth; - streamSchema?: S; - httpsOptions?: HttpsOptions; - enforceAppCheck?: boolean; - consumeAppCheckToken?: boolean; -} - -/** - * Creates a flow backed by Cloud Functions for Firebase gen2 HTTPS function. - */ -export function onFlow< - I extends z.ZodTypeAny, - O extends z.ZodTypeAny, - S extends z.ZodTypeAny, ->( - genkit: Genkit, - config: FunctionFlowConfig, - steps: FlowFn -): FunctionFlow { - const flow = genkit.defineFlow( - { - ...config, - }, - steps - ); - - const wrapped = wrapHttpsFlow(genkit, flow, config); - - const funcFlow = wrapped as FunctionFlow; - funcFlow.flow = flow; - return funcFlow; -} - -function wrapHttpsFlow< - I extends z.ZodTypeAny, - O extends z.ZodTypeAny, - S extends z.ZodTypeAny, ->( - genkit: Genkit, - flow: Flow, - config: FunctionFlowConfig -): HttpsFunction { - return onRequest( - { - ...config.httpsOptions, - memory: config.httpsOptions?.memory || '512MiB', - }, - async (req, res) => { - if (config.enforceAppCheck) { - if ( - !(await appCheckValid( - req.headers['x-firebase-appcheck'], - !!config.consumeAppCheckToken - )) - ) { - const respBody = { - error: { - status: 'UNAUTHENTICATED', - message: 'Unauthorized', - }, - }; - logger.logStructured(`Response[/${flow.name}]`, { - path: `/${flow.name}`, - code: 401, - body: respBody, - }); - res.status(401).send(respBody).end(); - return; - } - } - - await config.authPolicy.provider(req, res, () => - expressHandler(flow, { - authPolicy: (context) => - config.authPolicy.policy(context.auth, context.input), - })(req, res, () => {}) - ); - } - ); -} - -async function appCheckValid( - tok: string | string[] | undefined, - consume: boolean -): Promise { - if (typeof tok !== 'string') return false; - initializeAppIfNecessary(); - try { - const appCheckClaims = await getAppCheck().verifyToken(tok, { consume }); - - if (consume && appCheckClaims.alreadyConsumed) return false; - return true; - } catch (e) { - return false; - } -} - -/** - * Indicates that no authorization is in effect. - * - * WARNING: If you are using Cloud Functions for Firebase with no IAM policy, - * this will allow anyone on the internet to execute this flow. - */ -export function noAuth(): FunctionFlowAuth { - return { - provider: (req, res, next) => next(), - policy: () => {}, - }; -} diff --git a/js/plugins/firebase/src/helpers.ts b/js/plugins/firebase/src/helpers.ts index 88248591f..8dbe46d28 100644 --- a/js/plugins/firebase/src/helpers.ts +++ b/js/plugins/firebase/src/helpers.ts @@ -15,121 +15,6 @@ */ import { getApps, initializeApp } from 'firebase-admin/app'; -import { StreamingCallback } from 'genkit'; -import { GoogleAuth } from 'google-auth-library'; - -// cached `GoogleAuth` client. -let auth: GoogleAuth; -function getAuthClient() { - // Lazy load GoogleAuth client. - if (!auth) { - auth = new GoogleAuth(); - } - return auth; -} - -const streamDelimiter = '\n'; - -export async function callHttpsFunction( - functionName: string, - location: string, - data: any, - streamingCallback?: StreamingCallback -) { - const auth = getAuthClient(); - let funcUrl = await getFunctionUrl(functionName, location); - if (!funcUrl) { - throw new Error(`Unable to retrieve uri for function at ${functionName}`); - } - const tokenClient = await auth.getIdTokenClient(funcUrl); - const token = await tokenClient.idTokenProvider.fetchIdToken(funcUrl); - - if (streamingCallback) { - funcUrl += '?stream=true'; - } - - const res = await fetch(funcUrl, { - method: 'POST', - body: JSON.stringify(data), - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - }, - }); - - if (streamingCallback) { - const reader = res.body!.getReader(); - const decoder = new TextDecoder(); - let buffer = ''; - while (true) { - const result = await reader.read(); - const decodedValue = decoder.decode(result.value); - if (decodedValue) { - buffer += decodedValue; - } - // If buffer includes the delimiter that means we are still recieving chunks. - while (buffer.includes(streamDelimiter)) { - streamingCallback( - JSON.parse(buffer.substring(0, buffer.indexOf(streamDelimiter))) - ); - buffer = buffer.substring( - buffer.indexOf(streamDelimiter) + streamDelimiter.length - ); - } - if (result.done) { - return buffer; - } - } - } - const responseText = await res.text(); - return responseText; -} - -const functionUrlCache = {} as Record; - -export async function getFunctionUrl(name, location) { - if (functionUrlCache[name]) { - return functionUrlCache[name]; - } - const auth = getAuthClient(); - const projectId = await auth.getProjectId(); - const url = - 'https://cloudfunctions.googleapis.com/v2beta/' + - `projects/${projectId}/locations/${location}/functions/${name}`; - - const client = await auth.getClient(); - const res = (await client.request({ url })) as any; - const uri = res.data?.serviceConfig?.uri; - if (!uri) { - throw new Error(`Unable to retrieve uri for function at ${url}`); - } - functionUrlCache[name] = uri; - return uri; -} - -/** - * Extracts error message from the given error object, or if input is not an error then just turn the error into a string. - */ -export function getErrorMessage(e: any): string { - if (e instanceof Error) { - return e.message; - } - return `${e}`; -} - -/** - * Extracts stack trace from the given error object, or if input is not an error then returns undefined. - */ -export function getErrorStack(e: any): string | undefined { - if (e instanceof Error) { - return e.stack; - } - return undefined; -} - -export function getLocation() { - return process.env['GCLOUD_LOCATION'] || 'us-central1'; -} export function initializeAppIfNecessary() { if (!getApps().length) { diff --git a/js/plugins/firebase/tests/auth_test.ts b/js/plugins/firebase/tests/auth_test.ts deleted file mode 100644 index d382ec313..000000000 --- a/js/plugins/firebase/tests/auth_test.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2024 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 { describe, expect, it } from '@jest/globals'; -import { firebaseAuth } from '../lib/auth.js'; - -describe('firebaseAuth', () => { - it('config unset throws', async () => { - const auth = firebaseAuth((user, input) => {}); - - expect(auth.policy(undefined, undefined)).rejects.toThrow( - 'Auth is required' - ); - }); - - it('not required ok', async () => { - const auth = firebaseAuth((user, input) => {}, { required: false }); - - expect(auth.policy(undefined, undefined)).resolves.not.toThrow(); - }); - - it('required throws', async () => { - const auth = firebaseAuth((user, input) => {}, { required: true }); - - expect(auth.policy(undefined, undefined)).rejects.toThrow( - 'Auth is required' - ); - }); - - it('config unset present ok', async () => { - const auth = firebaseAuth((user, input) => {}); - - expect(auth.policy({}, undefined)).resolves.not.toThrow(); - }); - - it('required present ok', async () => { - const auth = firebaseAuth((user, input) => {}, { required: true }); - - expect(auth.policy({}, undefined)).resolves.not.toThrow(); - }); -}); diff --git a/js/plugins/firebase/tests/functions_test.ts b/js/plugins/firebase/tests/functions_test.ts deleted file mode 100644 index 14e050bdc..000000000 --- a/js/plugins/firebase/tests/functions_test.ts +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright 2024 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 { afterEach, beforeEach, describe, expect, it } from '@jest/globals'; -import * as express from 'express'; -import { initializeApp } from 'firebase/app'; -import { getFunctions, httpsCallableFromURL } from 'firebase/functions'; -import { Genkit, genkit } from 'genkit'; -import { runFlow, streamFlow } from 'genkit/beta/client'; -import * as getPort from 'get-port'; -import * as http from 'http'; -import { RequestWithAuth, noAuth, onFlow } from '../lib/functions.js'; - -describe('function', () => { - let ai: Genkit; - let server: http.Server; - let port: number; - - beforeEach(async () => { - ai = genkit({}); - - const authPolicy = { - provider: async (req, resp, next) => { - (req as RequestWithAuth).auth = { - user: - req.header('authorization') === 'open sesame' - ? 'Ali Baba' - : '40 thieves', - }; - next(); - }, - policy: (auth, input) => { - if (auth.user !== 'Ali Baba') { - throw new Error('not authorized'); - } - }, - }; - - const flow = onFlow( - ai, - { - name: 'flow', - authPolicy: noAuth(), - }, - async (input) => { - return `hi ${input}`; - } - ); - - const streamingFlow = onFlow( - ai, - { - name: 'streamingFlow', - authPolicy: noAuth(), - }, - async (input, { sendChunk }) => { - sendChunk({ chubk: 1 }); - sendChunk({ chubk: 2 }); - sendChunk({ chubk: 3 }); - - return `hi ${input}`; - } - ); - - const flowWithAuth = onFlow( - ai, - { - name: 'flowWithAuth', - authPolicy: authPolicy, - }, - async (input, { context }) => { - return `hi ${input} - ${JSON.stringify(context?.auth)}`; - } - ); - - const streamingFlowWithAuth = onFlow( - ai, - { - name: 'streamingFlowWithAuth', - authPolicy: authPolicy, - }, - async (input, { context, sendChunk }) => { - sendChunk({ chubk: 1 }); - sendChunk({ chubk: 2 }); - sendChunk({ chubk: 3 }); - - return `hi ${input} - ${JSON.stringify(context?.auth)}`; - } - ); - const app = express(); - app.use(express.json()); - port = await getPort(); - app.post('/flow', flow); - app.post('/flowWithAuth', flowWithAuth); - app.post('/streamingFlow', streamingFlow); - app.post('/streamingFlowWithAuth', streamingFlowWithAuth); - server = app.listen(port, () => { - console.log(`Example app listening on port ${port}`); - }); - }); - - afterEach(() => { - server.close(); - }); - - it('should call as an express route using callable functions SDK', async () => { - const functions = getFunctions(initializeApp({})); - - const callableFlow = httpsCallableFromURL( - functions, - `http://localhost:${port}/flow` - ); - - const callableResponse = await callableFlow('Pavel'); - expect(callableResponse.data).toBe('hi Pavel'); - }); - - it('should stream as an express route using callable functions SDK', async () => { - const functions = getFunctions(initializeApp({})); - - const callableFlow = httpsCallableFromURL( - functions, - `http://localhost:${port}/streamingFlow` - ); - - const callableResponse = await callableFlow.stream('Pavel'); - const chunks: any[] = []; - for await (const chunk of callableResponse.stream) { - chunks.push(chunk); - } - expect(await callableResponse.data).toEqual('hi Pavel'); - expect(chunks).toStrictEqual([{ chubk: 1 }, { chubk: 2 }, { chubk: 3 }]); - }); - - it('should call as an express route using genkit client SDK', async () => { - const result = await runFlow({ - url: `http://localhost:${port}/flowWithAuth`, - input: 'Pavel', - headers: { - Authorization: 'open sesame', - }, - }); - - expect(result).toBe('hi Pavel - {"user":"Ali Baba"}'); - }); - - it('should call as an express route using genkit client SDK', async () => { - const result = await streamFlow({ - url: `http://localhost:${port}/streamingFlowWithAuth`, - input: 'Pavel', - headers: { - Authorization: 'open sesame', - }, - }); - - const chunks: any[] = []; - for await (const chunk of result.stream) { - chunks.push(chunk); - } - - expect(await result.output).toBe('hi Pavel - {"user":"Ali Baba"}'); - expect(chunks).toStrictEqual([{ chubk: 1 }, { chubk: 2 }, { chubk: 3 }]); - }); -}); diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 05e8e7657..3555a83c3 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -172,9 +172,6 @@ importers: '@genkit-ai/express': specifier: workspace:* version: link:../plugins/express - '@genkit-ai/firebase': - specifier: workspace:* - version: link:../plugins/firebase '@genkit-ai/googleai': specifier: workspace:* version: link:../plugins/googleai @@ -184,6 +181,9 @@ importers: data-urls: specifier: ^5.0.0 version: 5.0.0 + firebase-functions: + specifier: ^6.3.1 + version: 6.3.1(firebase-admin@12.3.1(encoding@0.1.13)) genkit: specifier: workspace:* version: link:../genkit @@ -419,30 +419,18 @@ importers: plugins/firebase: dependencies: - '@genkit-ai/express': - specifier: workspace:^ - version: link:../express '@genkit-ai/google-cloud': specifier: workspace:^ version: link:../google-cloud '@google-cloud/firestore': specifier: ^7.6.0 version: 7.6.0(encoding@0.1.13) - express: - specifier: ^4.21.0 - version: 4.21.0 firebase-admin: specifier: '>=12.2' version: 12.3.1(encoding@0.1.13) - firebase-functions: - specifier: '>=4.8' - version: 4.8.1(encoding@0.1.13)(firebase-admin@12.3.1(encoding@0.1.13)) genkit: specifier: workspace:^ version: link:../../genkit - google-auth-library: - specifier: ^9.6.3 - version: 9.7.0(encoding@0.1.13) devDependencies: '@jest/globals': specifier: ^29.7.0 @@ -453,12 +441,6 @@ importers: '@types/node': specifier: ^20.11.16 version: 20.11.30 - firebase: - specifier: ^11.1.0 - version: 11.1.0 - get-port: - specifier: ^5.1.0 - version: 5.1.0 jest: specifier: ^29.7.0 version: 29.7.0(@types/node@20.11.30)(ts-node@10.9.2(@types/node@20.11.30)(typescript@4.9.5)) @@ -937,8 +919,8 @@ importers: specifier: workspace:* version: link:../../plugins/evaluators '@genkit-ai/firebase': - specifier: workspace:* - version: link:../../plugins/firebase + specifier: ^0.9.12 + version: 0.9.12(@google-cloud/firestore@7.11.0(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@12.3.1(encoding@0.1.13))(firebase-functions@6.3.1(firebase-admin@12.3.1(encoding@0.1.13)))(genkit@genkit) '@genkit-ai/google-cloud': specifier: workspace:* version: link:../../plugins/google-cloud @@ -951,6 +933,9 @@ importers: firebase-admin: specifier: '>=12.2' version: 12.3.1(encoding@0.1.13) + firebase-functions: + specifier: ^6.3.1 + version: 6.3.1(firebase-admin@12.3.1(encoding@0.1.13)) genkit: specifier: workspace:* version: link:../../genkit @@ -2271,249 +2256,52 @@ packages: '@fastify/busboy@3.0.0': resolution: {integrity: sha512-83rnH2nCvclWaPQQKvkJ2pdOjG4TZyEVuFDnlOF6KP08lDaaceVyw/W63mDuafQT+MKHCvXIPpE5uYWeM0rT4w==} - '@firebase/analytics-compat@0.2.16': - resolution: {integrity: sha512-Q/s+u/TEMSb2EDJFQMGsOzpSosybBl8HuoSEMyGZ99+0Pu7SIR9MPDGUjc8PKiCFQWDJ3QXxgqh1d/rujyAMbA==} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/analytics-types@0.8.3': - resolution: {integrity: sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==} - - '@firebase/analytics@0.10.10': - resolution: {integrity: sha512-Psdo7c9g2SLAYh6u1XRA+RZ7ab2JfBVuAt/kLzXkhKZL/gS2cQUCMsOW5p0RIlDPRKqpdNSmvujd2TeRWLKOkQ==} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/app-check-compat@0.3.17': - resolution: {integrity: sha512-a/eadrGsY0MVCBPhrNbKUhoYpms4UKTYLKO7nswwSFVsm3Rw6NslQQCNLfvljcDqP4E7alQDRGJXjkxd/5gJ+Q==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app-compat': 0.x - '@firebase/app-check-interop-types@0.3.1': resolution: {integrity: sha512-NILZbe6RH3X1pZmJnfOfY2gLIrlKmrkUMMrrK6VSXHcSE0eQv28xFEcw16D198i9JYZpy5Kwq394My62qCMaIw==} - '@firebase/app-check-interop-types@0.3.3': - resolution: {integrity: sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==} - - '@firebase/app-check-types@0.5.3': - resolution: {integrity: sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==} - - '@firebase/app-check@0.8.10': - resolution: {integrity: sha512-DWFfxxif/t+Ow4MmRUevDX+A3hVxm1rUf6y5ZP4sIomfnVCO1NNahqtsv9rb1/tKGkTeoVT40weiTS/WjQG1mA==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/app-compat@0.2.47': - resolution: {integrity: sha512-TdEWGDp6kSwuO1mxiM2Fe39eLWygfyzqTZcoU3aPV0viqqphPCbBBnVjPbFJErZ4+yaS7uCWXEbFEP9m5/COKA==} - engines: {node: '>=18.0.0'} - '@firebase/app-types@0.9.1': resolution: {integrity: sha512-nFGqTYsnDFn1oXf1tCwPAc+hQPxyvBT/QB7qDjwK+IDYThOn63nGhzdUTXxVD9Ca8gUY/e5PQMngeo0ZW/E3uQ==} - '@firebase/app-types@0.9.3': - resolution: {integrity: sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==} - - '@firebase/app@0.10.17': - resolution: {integrity: sha512-53sIYyAnYEPIZdaxuyq5OST7j4KBc2pqmktz+tEb1BIUSbXh8Gp4k/o6qzLelLpm4ngrBz7SRN0PZJqNRAyPog==} - engines: {node: '>=18.0.0'} - - '@firebase/auth-compat@0.5.16': - resolution: {integrity: sha512-YlYwJMBqAyv0ESy3jDUyshMhZlbUiwAm6B6+uUmigNDHU+uq7j4SFiDJEZlFFIz397yBzKn06SUdqutdQzGnCA==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app-compat': 0.x - '@firebase/auth-interop-types@0.2.2': resolution: {integrity: sha512-k3NA28Jfoo0+o391bFjoV9X5QLnUL1WbLhZZRbTQhZdmdGYJfX8ixtNNlHsYQ94bwG0QRbsmvkzDnzuhHrV11w==} - '@firebase/auth-interop-types@0.2.4': - resolution: {integrity: sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==} - - '@firebase/auth-types@0.12.3': - resolution: {integrity: sha512-Zq9zI0o5hqXDtKg6yDkSnvMCMuLU6qAVS51PANQx+ZZX5xnzyNLEBO3GZgBUPsV5qIMFhjhqmLDxUqCbnAYy2A==} - peerDependencies: - '@firebase/app-types': 0.x - '@firebase/util': 1.x - - '@firebase/auth@1.8.1': - resolution: {integrity: sha512-LX9N/Cf5Z35r5yqm2+5M3+2bRRe/+RFaa/+u4HDni7TA27C/Xm4XHLKcWcLg1BzjrS4zngSaBEOSODvp6RFOqQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app': 0.x - '@react-native-async-storage/async-storage': ^1.18.1 - peerDependenciesMeta: - '@react-native-async-storage/async-storage': - optional: true - - '@firebase/component@0.6.11': - resolution: {integrity: sha512-eQbeCgPukLgsKD0Kw5wQgsMDX5LeoI1MIrziNDjmc6XDq5ZQnuUymANQgAb2wp1tSF9zDSXyxJmIUXaKgN58Ug==} - engines: {node: '>=18.0.0'} - '@firebase/component@0.6.6': resolution: {integrity: sha512-pp7sWqHmAAlA3os6ERgoM3k5Cxff510M9RLXZ9Mc8KFKMBc2ct3RkZTWUF7ixJNvMiK/iNgRLPDrLR2gtRJ9iQ==} - '@firebase/data-connect@0.1.3': - resolution: {integrity: sha512-FbAQpWNHownJx1VTCQI4ydbWGOZmSWXoFlirQn3ItHqsLJYSywqxSgDafzvyooifFh3J/2WqaM8y9hInnPcsTw==} - peerDependencies: - '@firebase/app': 0.x - '@firebase/database-compat@1.0.4': resolution: {integrity: sha512-GEEDAvsSMAkqy0BIFSVtFzoOIIcKHFfDM4aXHtWL/JCaNn4OOjH7td73jDfN3ALvpIN4hQki0FcxQ89XjqaTjQ==} - '@firebase/database-compat@2.0.1': - resolution: {integrity: sha512-IsFivOjdE1GrjTeKoBU/ZMenESKDXidFDzZzHBPQ/4P20ptGdrl3oLlWrV/QJqJ9lND4IidE3z4Xr5JyfUW1vg==} - engines: {node: '>=18.0.0'} - '@firebase/database-types@1.0.2': resolution: {integrity: sha512-JRigr5JNLEHqOkI99tAGHDZF47469/cJz1tRAgGs8Feh+3ZmQy/vVChSqwMp2DuVUGp9PlmGsNSlpINJ/hDuIA==} - '@firebase/database-types@1.0.7': - resolution: {integrity: sha512-I7zcLfJXrM0WM+ksFmFdAMdlq/DFmpeMNa+/GNsLyFo5u/lX5zzkPzGe3srVWqaBQBY5KprylDGxOsP6ETfL0A==} - - '@firebase/database@1.0.10': - resolution: {integrity: sha512-sWp2g92u7xT4BojGbTXZ80iaSIaL6GAL0pwvM0CO/hb0nHSnABAqsH7AhnWGsGvXuEvbPr7blZylPaR9J+GSuQ==} - engines: {node: '>=18.0.0'} - '@firebase/database@1.0.4': resolution: {integrity: sha512-k84cXh+dtpzvY6yOhfyr1B+I1vjvSMtmlqotE0lTNVylc8m5nmOohjzpTLEQDrBWvwACX/VP5fEyajAdmnOKqA==} - '@firebase/firestore-compat@0.3.40': - resolution: {integrity: sha512-18HopMN811KYBc9Ptpr1Rewwio0XF09FF3jc5wtV6rGyAs815SlFFw5vW7ZeLd43zv9tlEc2FzM0H+5Vr9ZRxw==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/firestore-types@3.0.3': - resolution: {integrity: sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==} - peerDependencies: - '@firebase/app-types': 0.x - '@firebase/util': 1.x - - '@firebase/firestore@4.7.5': - resolution: {integrity: sha512-OO3rHvjC07jL2ITN255xH/UzCVSvh6xG8oTzQdFScQvFbcm1fjCL1hgAdpDZcx3vVcKMV+6ktr8wbllkB8r+FQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/functions-compat@0.3.17': - resolution: {integrity: sha512-oj2XV8YsJYutyPCRYUfbN6swmfrL6zar0/qtqZsKT7P7btOiYRl+lD6fxtQaT+pKE5YgOBGZW//kLPZfY0jWhw==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/functions-types@0.6.3': - resolution: {integrity: sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==} - - '@firebase/functions@0.12.0': - resolution: {integrity: sha512-plTtzY/nT0jOgHzT0vB9qch4FpHFOhCnR8HhYBqqdArG6GOQMIruKZbiTyLybO8bcaaNgQ6kSm9yohGUwxHcIw==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/installations-compat@0.2.11': - resolution: {integrity: sha512-SHRgw5LTa6v8LubmJZxcOCwEd1MfWQPUtKdiuCx2VMWnapX54skZd1PkQg0K4l3k+4ujbI2cn7FE6Li9hbChBw==} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/installations-types@0.5.3': - resolution: {integrity: sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==} - peerDependencies: - '@firebase/app-types': 0.x - - '@firebase/installations@0.6.11': - resolution: {integrity: sha512-w8fY8mw6fxJzsZM2ufmTtomopXl1+bn/syYon+Gpn+0p0nO1cIUEVEFrFazTLaaL9q1CaVhc3HmseRTsI3igAA==} - peerDependencies: - '@firebase/app': 0.x - '@firebase/logger@0.4.1': resolution: {integrity: sha512-tTIixB5UJbG9ZHSGZSZdX7THr3KWOLrejZ9B7jYsm6fpwgRNngKznQKA2wgYVyvBc1ta7dGFh9NtJ8n7qfiYIw==} - '@firebase/logger@0.4.4': - resolution: {integrity: sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==} - engines: {node: '>=18.0.0'} - - '@firebase/messaging-compat@0.2.15': - resolution: {integrity: sha512-mEKKASRvRWq1aBNHgioGsOYR2c5nBZpO7k90K794zjKe0WkGNf0k7PLs5SlCf8FKnzumEkhTAp/SjYxovuxa8A==} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/messaging-interop-types@0.2.3': - resolution: {integrity: sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==} - - '@firebase/messaging@0.12.15': - resolution: {integrity: sha512-Bz+qvWNEwEWAbYtG4An8hgcNco6NWNoNLuLbGVwPL2fAoCF1zz+dcaBp+iTR2+K199JyRyDT9yDPAXhNHNDaKQ==} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/performance-compat@0.2.11': - resolution: {integrity: sha512-DqeNBy51W2xzlklyC7Ht9JQ94HhTA08PCcM4MDeyG/ol3fqum/+YgtHWQ2IQuduqH9afETthZqLwCZiSgY7hiA==} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/performance-types@0.2.3': - resolution: {integrity: sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==} - - '@firebase/performance@0.6.11': - resolution: {integrity: sha512-FlkJFeqLlIeh5T4Am3uE38HVzggliDIEFy/fErEc1faINOUFCb6vQBEoNZGaXvRnTR8lh3X/hP7tv37C7BsK9g==} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/remote-config-compat@0.2.11': - resolution: {integrity: sha512-zfIjpwPrGuIOZDmduukN086qjhZ1LnbJi/iYzgua+2qeTlO0XdlE1v66gJPwygGB3TOhT0yb9EiUZ3nBNttMqg==} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/remote-config-types@0.3.3': - resolution: {integrity: sha512-YlRI9CHxrk3lpQuFup9N1eohpwdWayKZUNZ/YeQ0PZoncJ66P32UsKUKqVXOaieTjJIOh7yH8JEzRdht5s+d6g==} - - '@firebase/remote-config@0.4.11': - resolution: {integrity: sha512-9z0rgKuws2nj+7cdiqF+NY1QR4na6KnuOvP+jQvgilDOhGtKOcCMq5XHiu66i73A9kFhyU6QQ2pHXxcmaq1pBw==} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/storage-compat@0.3.14': - resolution: {integrity: sha512-Ok5FmXJiapaNAOQ8W8qppnfwgP8540jw2B8M0c4TFZqF4BD+CoKBxW0dRtOuLNGadLhzqqkDZZZtkexxrveQqA==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/storage-types@0.8.3': - resolution: {integrity: sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==} - peerDependencies: - '@firebase/app-types': 0.x - '@firebase/util': 1.x - - '@firebase/storage@0.13.4': - resolution: {integrity: sha512-b1KaTTRiMupFurIhpGIbReaWev0k5O3ouTHkAPcEssT+FvU3q/1JwzvkX4+ZdB60Fc43Mbp8qQ1gWfT0Z2FP9Q==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/util@1.10.2': - resolution: {integrity: sha512-qnSHIoE9FK+HYnNhTI8q14evyqbc/vHRivfB4TgCIUOl4tosmKSQlp7ltymOlMP4xVIJTg5wrkfcZ60X4nUf7Q==} - engines: {node: '>=18.0.0'} - '@firebase/util@1.9.5': resolution: {integrity: sha512-PP4pAFISDxsf70l3pEy34Mf3GkkUcVQ3MdKp6aSVb7tcpfUQxnsdV7twDd8EkfB6zZylH6wpUAoangQDmCUMqw==} - '@firebase/vertexai@1.0.2': - resolution: {integrity: sha512-4dC9m2nD0tkfKJT5v+i27tELrmUePjFXW3CDAxhVHUEv647B2R7kqpGQnyPkNEeaXkCr76THe7GGg35EWn4lDw==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app': 0.x - '@firebase/app-types': 0.x - - '@firebase/webchannel-wrapper@1.0.3': - resolution: {integrity: sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==} - '@genkit-ai/ai@1.0.0-rc.15': resolution: {integrity: sha512-cCB0Lp1Q7umV3fAKvfydz99CEeSq8n5KBfp+3oAIwFWgRvRTBgw0Y5tLtuis4TptCD2ZqqnoiedPW+3JXQU+VQ==} '@genkit-ai/core@1.0.0-rc.15': resolution: {integrity: sha512-eq6nRTPELk7/wkhVXdaAgV1sVrLFgzcdiSeezTpsyhISH9gVWzcSHsNPwxdAR3K+szgo5PkomZQT6JIQKrlxsQ==} + '@genkit-ai/firebase@0.9.12': + resolution: {integrity: sha512-PbEAPO3GTENaJR5RUqrq88YIVxgkVi2pc82kOCrkJDpw/+PcyRIZcgvbQYSAzB932yVU0sy33zzdUfg9t2thMA==} + peerDependencies: + '@google-cloud/firestore': ^7.6.0 + firebase-admin: '>=12.2' + firebase-functions: '>=4.8' + genkit: 0.9.12 + + '@genkit-ai/google-cloud@0.9.12': + resolution: {integrity: sha512-hYDvdQmGdI/JC6hUa6hMs6+Kc04wmi+/54zJIB+EOiNLd6fJ193B+q/pHqI4UxvofJPsf9C6VTK5Q54+yyQm7w==} + peerDependencies: + genkit: 0.9.12 + '@gerrit0/mini-shiki@1.24.4': resolution: {integrity: sha512-YEHW1QeAg6UmxEmswiQbOVEg1CW22b1XUD/lNTliOsu0LD0wqoyleFMnmbTp697QE0pcadQiR5cVtbbAPncvpw==} @@ -2619,10 +2407,6 @@ packages: resolution: {integrity: sha512-MqBisuxTkYvPFnEiu+dag3xG/NBUDzSbAFAWlzfkGnQkjVZ6by3h4atbBc+Ikqup1z5BfB4BN18gKWR1YyppNw==} engines: {node: '>=12.10.0'} - '@grpc/grpc-js@1.9.15': - resolution: {integrity: sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==} - engines: {node: ^8.13.0 || >=10.10.0} - '@grpc/proto-loader@0.7.12': resolution: {integrity: sha512-DCVwMxqYzpUCiDMl7hQ384FqP4T3DbNpXU8pt681l3UWCip1WUiD5JrkImUwCB9a7f2cq4CUTmi5r/xIMRPY1Q==} engines: {node: '>=6'} @@ -3855,9 +3639,6 @@ packages: '@types/express@4.17.21': resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} - '@types/express@4.17.3': - resolution: {integrity: sha512-I8cGRJj3pyOLs/HndoP+25vOqhqWkAZsWMEmq1qXy/b/M3ppufecUwaK2/TVDVxcV61/iSdhykUjQQ2DLSrTdg==} - '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} @@ -4773,15 +4554,12 @@ packages: resolution: {integrity: sha512-vEr3s3esl8nPIA9r/feDT4nzIXCfov1CyyCSpMQWp6x63Q104qke0MEGZlrHUZVROtl8FLus6niP/M9I1s4VBA==} engines: {node: '>=14'} - firebase-functions@4.8.1: - resolution: {integrity: sha512-SHA7ZUlG+MOOsKyp+D4vhSyF4FsJMD+qyVUkTcPry6wbbxDitv9k4xgUPXffhbiokxFi1AbeckA8SGD41AZiCg==} + firebase-functions@6.3.1: + resolution: {integrity: sha512-LTbmsEkSgaOhzTzGUoF7dv906JJJW89o0/spXgnU8gASyR8JLMrCqwV7FnWLso5hyF0fUqNPaEEw/TzLdZMVXw==} engines: {node: '>=14.10.0'} hasBin: true peerDependencies: - firebase-admin: ^10.0.0 || ^11.0.0 || ^12.0.0 - - firebase@11.1.0: - resolution: {integrity: sha512-3OoNW3vBXmBLYJvcwbPCwfluptbDVp2zZYjrfHPVFAXfPgmyy/LWjidt+Sw2WNvRelsG0v++WN2Wor6J3OwDRg==} + firebase-admin: ^11.10.0 || ^12.0.0 || ^13.0.0 flat@5.0.2: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} @@ -5066,9 +4844,6 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - idb@7.1.1: - resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} - ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -7472,123 +7247,17 @@ snapshots: '@fastify/busboy@3.0.0': {} - '@firebase/analytics-compat@0.2.16(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17)': - dependencies: - '@firebase/analytics': 0.10.10(@firebase/app@0.10.17) - '@firebase/analytics-types': 0.8.3 - '@firebase/app-compat': 0.2.47 - '@firebase/component': 0.6.11 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/analytics-types@0.8.3': {} - - '@firebase/analytics@0.10.10(@firebase/app@0.10.17)': - dependencies: - '@firebase/app': 0.10.17 - '@firebase/component': 0.6.11 - '@firebase/installations': 0.6.11(@firebase/app@0.10.17) - '@firebase/logger': 0.4.4 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - - '@firebase/app-check-compat@0.3.17(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17)': - dependencies: - '@firebase/app-check': 0.8.10(@firebase/app@0.10.17) - '@firebase/app-check-types': 0.5.3 - '@firebase/app-compat': 0.2.47 - '@firebase/component': 0.6.11 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - '@firebase/app-check-interop-types@0.3.1': {} - '@firebase/app-check-interop-types@0.3.3': {} - - '@firebase/app-check-types@0.5.3': {} - - '@firebase/app-check@0.8.10(@firebase/app@0.10.17)': - dependencies: - '@firebase/app': 0.10.17 - '@firebase/component': 0.6.11 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - - '@firebase/app-compat@0.2.47': - dependencies: - '@firebase/app': 0.10.17 - '@firebase/component': 0.6.11 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - '@firebase/app-types@0.9.1': {} - '@firebase/app-types@0.9.3': {} - - '@firebase/app@0.10.17': - dependencies: - '@firebase/component': 0.6.11 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.10.2 - idb: 7.1.1 - tslib: 2.6.2 - - '@firebase/auth-compat@0.5.16(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17)': - dependencies: - '@firebase/app-compat': 0.2.47 - '@firebase/auth': 1.8.1(@firebase/app@0.10.17) - '@firebase/auth-types': 0.12.3(@firebase/app-types@0.9.3)(@firebase/util@1.10.2) - '@firebase/component': 0.6.11 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - '@react-native-async-storage/async-storage' - '@firebase/auth-interop-types@0.2.2': {} - '@firebase/auth-interop-types@0.2.4': {} - - '@firebase/auth-types@0.12.3(@firebase/app-types@0.9.3)(@firebase/util@1.10.2)': - dependencies: - '@firebase/app-types': 0.9.3 - '@firebase/util': 1.10.2 - - '@firebase/auth@1.8.1(@firebase/app@0.10.17)': - dependencies: - '@firebase/app': 0.10.17 - '@firebase/component': 0.6.11 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - - '@firebase/component@0.6.11': - dependencies: - '@firebase/util': 1.10.2 - tslib: 2.8.1 - '@firebase/component@0.6.6': dependencies: '@firebase/util': 1.9.5 tslib: 2.8.1 - '@firebase/data-connect@0.1.3(@firebase/app@0.10.17)': - dependencies: - '@firebase/app': 0.10.17 - '@firebase/auth-interop-types': 0.2.4 - '@firebase/component': 0.6.11 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - '@firebase/database-compat@1.0.4': dependencies: '@firebase/component': 0.6.6 @@ -7598,35 +7267,11 @@ snapshots: '@firebase/util': 1.9.5 tslib: 2.8.1 - '@firebase/database-compat@2.0.1': - dependencies: - '@firebase/component': 0.6.11 - '@firebase/database': 1.0.10 - '@firebase/database-types': 1.0.7 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - '@firebase/database-types@1.0.2': dependencies: '@firebase/app-types': 0.9.1 '@firebase/util': 1.9.5 - '@firebase/database-types@1.0.7': - dependencies: - '@firebase/app-types': 0.9.3 - '@firebase/util': 1.10.2 - - '@firebase/database@1.0.10': - dependencies: - '@firebase/app-check-interop-types': 0.3.3 - '@firebase/auth-interop-types': 0.2.4 - '@firebase/component': 0.6.11 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.10.2 - faye-websocket: 0.11.4 - tslib: 2.6.2 - '@firebase/database@1.0.4': dependencies: '@firebase/app-check-interop-types': 0.3.1 @@ -7637,201 +7282,14 @@ snapshots: faye-websocket: 0.11.4 tslib: 2.8.1 - '@firebase/firestore-compat@0.3.40(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17)': - dependencies: - '@firebase/app-compat': 0.2.47 - '@firebase/component': 0.6.11 - '@firebase/firestore': 4.7.5(@firebase/app@0.10.17) - '@firebase/firestore-types': 3.0.3(@firebase/app-types@0.9.3)(@firebase/util@1.10.2) - '@firebase/util': 1.10.2 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - '@firebase/firestore-types@3.0.3(@firebase/app-types@0.9.3)(@firebase/util@1.10.2)': - dependencies: - '@firebase/app-types': 0.9.3 - '@firebase/util': 1.10.2 - - '@firebase/firestore@4.7.5(@firebase/app@0.10.17)': - dependencies: - '@firebase/app': 0.10.17 - '@firebase/component': 0.6.11 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.10.2 - '@firebase/webchannel-wrapper': 1.0.3 - '@grpc/grpc-js': 1.9.15 - '@grpc/proto-loader': 0.7.13 - tslib: 2.6.2 - - '@firebase/functions-compat@0.3.17(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17)': - dependencies: - '@firebase/app-compat': 0.2.47 - '@firebase/component': 0.6.11 - '@firebase/functions': 0.12.0(@firebase/app@0.10.17) - '@firebase/functions-types': 0.6.3 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/functions-types@0.6.3': {} - - '@firebase/functions@0.12.0(@firebase/app@0.10.17)': - dependencies: - '@firebase/app': 0.10.17 - '@firebase/app-check-interop-types': 0.3.3 - '@firebase/auth-interop-types': 0.2.4 - '@firebase/component': 0.6.11 - '@firebase/messaging-interop-types': 0.2.3 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - - '@firebase/installations-compat@0.2.11(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17)': - dependencies: - '@firebase/app-compat': 0.2.47 - '@firebase/component': 0.6.11 - '@firebase/installations': 0.6.11(@firebase/app@0.10.17) - '@firebase/installations-types': 0.5.3(@firebase/app-types@0.9.3) - '@firebase/util': 1.10.2 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - '@firebase/installations-types@0.5.3(@firebase/app-types@0.9.3)': - dependencies: - '@firebase/app-types': 0.9.3 - - '@firebase/installations@0.6.11(@firebase/app@0.10.17)': - dependencies: - '@firebase/app': 0.10.17 - '@firebase/component': 0.6.11 - '@firebase/util': 1.10.2 - idb: 7.1.1 - tslib: 2.6.2 - '@firebase/logger@0.4.1': dependencies: tslib: 2.8.1 - '@firebase/logger@0.4.4': - dependencies: - tslib: 2.8.1 - - '@firebase/messaging-compat@0.2.15(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17)': - dependencies: - '@firebase/app-compat': 0.2.47 - '@firebase/component': 0.6.11 - '@firebase/messaging': 0.12.15(@firebase/app@0.10.17) - '@firebase/util': 1.10.2 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/messaging-interop-types@0.2.3': {} - - '@firebase/messaging@0.12.15(@firebase/app@0.10.17)': - dependencies: - '@firebase/app': 0.10.17 - '@firebase/component': 0.6.11 - '@firebase/installations': 0.6.11(@firebase/app@0.10.17) - '@firebase/messaging-interop-types': 0.2.3 - '@firebase/util': 1.10.2 - idb: 7.1.1 - tslib: 2.6.2 - - '@firebase/performance-compat@0.2.11(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17)': - dependencies: - '@firebase/app-compat': 0.2.47 - '@firebase/component': 0.6.11 - '@firebase/logger': 0.4.4 - '@firebase/performance': 0.6.11(@firebase/app@0.10.17) - '@firebase/performance-types': 0.2.3 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/performance-types@0.2.3': {} - - '@firebase/performance@0.6.11(@firebase/app@0.10.17)': - dependencies: - '@firebase/app': 0.10.17 - '@firebase/component': 0.6.11 - '@firebase/installations': 0.6.11(@firebase/app@0.10.17) - '@firebase/logger': 0.4.4 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - - '@firebase/remote-config-compat@0.2.11(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17)': - dependencies: - '@firebase/app-compat': 0.2.47 - '@firebase/component': 0.6.11 - '@firebase/logger': 0.4.4 - '@firebase/remote-config': 0.4.11(@firebase/app@0.10.17) - '@firebase/remote-config-types': 0.3.3 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/remote-config-types@0.3.3': {} - - '@firebase/remote-config@0.4.11(@firebase/app@0.10.17)': - dependencies: - '@firebase/app': 0.10.17 - '@firebase/component': 0.6.11 - '@firebase/installations': 0.6.11(@firebase/app@0.10.17) - '@firebase/logger': 0.4.4 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - - '@firebase/storage-compat@0.3.14(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17)': - dependencies: - '@firebase/app-compat': 0.2.47 - '@firebase/component': 0.6.11 - '@firebase/storage': 0.13.4(@firebase/app@0.10.17) - '@firebase/storage-types': 0.8.3(@firebase/app-types@0.9.3)(@firebase/util@1.10.2) - '@firebase/util': 1.10.2 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - '@firebase/storage-types@0.8.3(@firebase/app-types@0.9.3)(@firebase/util@1.10.2)': - dependencies: - '@firebase/app-types': 0.9.3 - '@firebase/util': 1.10.2 - - '@firebase/storage@0.13.4(@firebase/app@0.10.17)': - dependencies: - '@firebase/app': 0.10.17 - '@firebase/component': 0.6.11 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - - '@firebase/util@1.10.2': - dependencies: - tslib: 2.6.2 - '@firebase/util@1.9.5': dependencies: tslib: 2.8.1 - '@firebase/vertexai@1.0.2(@firebase/app-types@0.9.3)(@firebase/app@0.10.17)': - dependencies: - '@firebase/app': 0.10.17 - '@firebase/app-check-interop-types': 0.3.3 - '@firebase/app-types': 0.9.3 - '@firebase/component': 0.6.11 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.10.2 - tslib: 2.6.2 - - '@firebase/webchannel-wrapper@1.0.3': {} - '@genkit-ai/ai@1.0.0-rc.15': dependencies: '@genkit-ai/core': 1.0.0-rc.15 @@ -7869,6 +7327,43 @@ snapshots: transitivePeerDependencies: - supports-color + '@genkit-ai/firebase@0.9.12(@google-cloud/firestore@7.11.0(encoding@0.1.13))(encoding@0.1.13)(firebase-admin@12.3.1(encoding@0.1.13))(firebase-functions@6.3.1(firebase-admin@12.3.1(encoding@0.1.13)))(genkit@genkit)': + dependencies: + '@genkit-ai/google-cloud': 0.9.12(encoding@0.1.13)(genkit@genkit) + '@google-cloud/firestore': 7.11.0(encoding@0.1.13) + express: 4.21.1 + firebase-admin: 12.3.1(encoding@0.1.13) + firebase-functions: 6.3.1(firebase-admin@12.3.1(encoding@0.1.13)) + genkit: link:genkit + google-auth-library: 9.14.2(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color + + '@genkit-ai/google-cloud@0.9.12(encoding@0.1.13)(genkit@genkit)': + dependencies: + '@google-cloud/logging-winston': 6.0.0(encoding@0.1.13)(winston@3.13.0) + '@google-cloud/opentelemetry-cloud-monitoring-exporter': 0.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.25.1(@opentelemetry/api@1.9.0))(encoding@0.1.13) + '@google-cloud/opentelemetry-cloud-trace-exporter': 2.4.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13) + '@google-cloud/opentelemetry-resource-util': 2.4.0(@opentelemetry/resources@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13) + '@opentelemetry/api': 1.9.0 + '@opentelemetry/auto-instrumentations-node': 0.49.1(@opentelemetry/api@1.9.0)(encoding@0.1.13) + '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pino': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-winston': 0.39.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.26.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-node': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.26.0(@opentelemetry/api@1.9.0) + genkit: link:genkit + google-auth-library: 9.14.2(encoding@0.1.13) + node-fetch: 3.3.2 + winston: 3.13.0 + transitivePeerDependencies: + - encoding + - supports-color + '@gerrit0/mini-shiki@1.24.4': dependencies: '@shikijs/engine-oniguruma': 1.24.2 @@ -7982,6 +7477,20 @@ snapshots: - encoding - supports-color + '@google-cloud/opentelemetry-cloud-monitoring-exporter@0.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.25.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)': + dependencies: + '@google-cloud/opentelemetry-resource-util': 2.4.0(@opentelemetry/resources@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13) + '@google-cloud/precise-date': 4.0.0 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.26.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.25.1(@opentelemetry/api@1.9.0) + google-auth-library: 9.14.2(encoding@0.1.13) + googleapis: 137.1.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color + '@google-cloud/opentelemetry-cloud-trace-exporter@2.4.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)': dependencies: '@google-cloud/opentelemetry-resource-util': 2.4.0(@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0))(encoding@0.1.13) @@ -7996,6 +7505,20 @@ snapshots: - encoding - supports-color + '@google-cloud/opentelemetry-cloud-trace-exporter@2.4.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.26.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)': + dependencies: + '@google-cloud/opentelemetry-resource-util': 2.4.0(@opentelemetry/resources@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13) + '@grpc/grpc-js': 1.10.10 + '@grpc/proto-loader': 0.7.13 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.26.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.26.0(@opentelemetry/api@1.9.0) + google-auth-library: 9.14.2(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color + '@google-cloud/opentelemetry-resource-util@2.4.0(@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)': dependencies: '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) @@ -8005,6 +7528,15 @@ snapshots: - encoding - supports-color + '@google-cloud/opentelemetry-resource-util@2.4.0(@opentelemetry/resources@1.26.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)': + dependencies: + '@opentelemetry/resources': 1.26.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.26.0 + gcp-metadata: 6.1.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color + '@google-cloud/paginator@5.0.0': dependencies: arrify: 2.0.1 @@ -8071,11 +7603,6 @@ snapshots: '@grpc/proto-loader': 0.7.12 '@js-sdsl/ordered-map': 4.4.2 - '@grpc/grpc-js@1.9.15': - dependencies: - '@grpc/proto-loader': 0.7.13 - '@types/node': 20.16.9 - '@grpc/proto-loader@0.7.12': dependencies: lodash.camelcase: 4.3.0 @@ -9334,12 +8861,6 @@ snapshots: '@types/qs': 6.9.14 '@types/serve-static': 1.15.5 - '@types/express@4.17.3': - dependencies: - '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.17.43 - '@types/serve-static': 1.15.5 - '@types/graceful-fs@4.1.9': dependencies: '@types/node': 20.16.9 @@ -10479,52 +10000,17 @@ snapshots: - encoding - supports-color - firebase-functions@4.8.1(encoding@0.1.13)(firebase-admin@12.3.1(encoding@0.1.13)): + firebase-functions@6.3.1(firebase-admin@12.3.1(encoding@0.1.13)): dependencies: '@types/cors': 2.8.17 - '@types/express': 4.17.3 + '@types/express': 4.17.21 cors: 2.8.5 - express: 4.21.0 + express: 4.21.1 firebase-admin: 12.3.1(encoding@0.1.13) - node-fetch: 2.7.0(encoding@0.1.13) protobufjs: 7.3.2 transitivePeerDependencies: - - encoding - supports-color - firebase@11.1.0: - dependencies: - '@firebase/analytics': 0.10.10(@firebase/app@0.10.17) - '@firebase/analytics-compat': 0.2.16(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17) - '@firebase/app': 0.10.17 - '@firebase/app-check': 0.8.10(@firebase/app@0.10.17) - '@firebase/app-check-compat': 0.3.17(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17) - '@firebase/app-compat': 0.2.47 - '@firebase/app-types': 0.9.3 - '@firebase/auth': 1.8.1(@firebase/app@0.10.17) - '@firebase/auth-compat': 0.5.16(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17) - '@firebase/data-connect': 0.1.3(@firebase/app@0.10.17) - '@firebase/database': 1.0.10 - '@firebase/database-compat': 2.0.1 - '@firebase/firestore': 4.7.5(@firebase/app@0.10.17) - '@firebase/firestore-compat': 0.3.40(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17) - '@firebase/functions': 0.12.0(@firebase/app@0.10.17) - '@firebase/functions-compat': 0.3.17(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17) - '@firebase/installations': 0.6.11(@firebase/app@0.10.17) - '@firebase/installations-compat': 0.2.11(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17) - '@firebase/messaging': 0.12.15(@firebase/app@0.10.17) - '@firebase/messaging-compat': 0.2.15(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17) - '@firebase/performance': 0.6.11(@firebase/app@0.10.17) - '@firebase/performance-compat': 0.2.11(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17) - '@firebase/remote-config': 0.4.11(@firebase/app@0.10.17) - '@firebase/remote-config-compat': 0.2.11(@firebase/app-compat@0.2.47)(@firebase/app@0.10.17) - '@firebase/storage': 0.13.4(@firebase/app@0.10.17) - '@firebase/storage-compat': 0.3.14(@firebase/app-compat@0.2.47)(@firebase/app-types@0.9.3)(@firebase/app@0.10.17) - '@firebase/util': 1.10.2 - '@firebase/vertexai': 1.0.2(@firebase/app-types@0.9.3)(@firebase/app@0.10.17) - transitivePeerDependencies: - - '@react-native-async-storage/async-storage' - flat@5.0.2: {} fn.name@1.1.0: {} @@ -10946,8 +10432,6 @@ snapshots: dependencies: safer-buffer: 2.1.2 - idb@7.1.1: {} - ieee754@1.2.1: optional: true diff --git a/js/testapps/dev-ui-gallery/package.json b/js/testapps/dev-ui-gallery/package.json index 1da156f87..068c70f12 100644 --- a/js/testapps/dev-ui-gallery/package.json +++ b/js/testapps/dev-ui-gallery/package.json @@ -25,11 +25,12 @@ "dependencies": { "@genkit-ai/dev-local-vectorstore": "workspace:*", "@genkit-ai/evaluator": "workspace:*", - "@genkit-ai/firebase": "workspace:*", + "@genkit-ai/firebase": "^0.9.12", "@genkit-ai/google-cloud": "workspace:*", "@genkit-ai/googleai": "workspace:*", "@genkit-ai/vertexai": "workspace:*", "firebase-admin": ">=12.2", + "firebase-functions": "^6.3.1", "genkit": "workspace:*", "genkitx-chromadb": "workspace:*", "genkitx-ollama": "workspace:*", diff --git a/js/testapps/dev-ui-gallery/src/main/flows-firebase-functions.ts b/js/testapps/dev-ui-gallery/src/main/flows-firebase-functions.ts index 06ad608f6..01153ec24 100644 --- a/js/testapps/dev-ui-gallery/src/main/flows-firebase-functions.ts +++ b/js/testapps/dev-ui-gallery/src/main/flows-firebase-functions.ts @@ -14,63 +14,37 @@ * limitations under the License. */ -import { firebaseAuth } from '@genkit-ai/firebase/auth'; -import { noAuth, onFlow } from '@genkit-ai/firebase/functions'; -import { gemini15Flash } from '@genkit-ai/googleai'; -import { DecodedIdToken } from 'firebase-admin/auth'; +import { onCallGenkit } from 'firebase-functions/https'; +import { defineSecret } from 'firebase-functions/params'; import { z } from 'genkit'; import { ai } from '../genkit.js'; -export const flowAuth = onFlow( - ai, +const apiKey = defineSecret('GOOGLE_GENAI_API_KEY'); + +const greetFlow = ai.defineFlow( { - name: 'flowAuth', + name: 'greet', inputSchema: z.string(), outputSchema: z.string(), - httpsOptions: { - cors: '*', - }, - authPolicy: firebaseAuth((user: DecodedIdToken) => { - if (!user.email_verified && !user.admin) { - throw new Error('Auth failed - email not verified'); - } - }), }, - async (language) => { - const prompt = `Say hello in language ${language}`; - - return await ai.run('call-llm', async () => { - const llmResponse = await ai.generate({ - model: gemini15Flash, - prompt: prompt, - }); - - return llmResponse.text; - }); + async (langauge: string) => { + const { output } = await ai.generate(`Say hello in language ${langauge}`); + return output; } ); -export const flowAuthNone = onFlow( - ai, +export const greetNoAuth = onCallGenkit( { - name: 'flowAuthNone', - inputSchema: z.string(), - outputSchema: z.string(), - httpsOptions: { - cors: '*', - }, - authPolicy: noAuth(), + secrets: [apiKey], }, - async (language) => { - const prompt = `Say hello in language ${language}`; - - return await ai.run('call-llm', async () => { - const llmResponse = await ai.generate({ - model: gemini15Flash, - prompt: prompt, - }); + greetFlow +); - return llmResponse.text; - }); - } +export const greetAuthAndAppCheck = onCallGenkit( + { + secrets: [apiKey], + authPolicy: (auth) => auth?.token?.email_verified && auth?.token?.admin, + enforceAppCheck: true, + }, + greetFlow ); diff --git a/js/testapps/esm/src/index.ts b/js/testapps/esm/src/index.ts index 7958c0f97..bf46617be 100644 --- a/js/testapps/esm/src/index.ts +++ b/js/testapps/esm/src/index.ts @@ -18,7 +18,6 @@ import { checks } from '@genkit-ai/checks'; import { devLocalVectorstore } from '@genkit-ai/dev-local-vectorstore'; import { genkitEval } from '@genkit-ai/evaluator'; import { enableFirebaseTelemetry } from '@genkit-ai/firebase'; -import { firebaseAuth } from '@genkit-ai/firebase/auth'; import { enableGoogleCloudTelemetry } from '@genkit-ai/google-cloud'; import { googleAI } from '@genkit-ai/googleai'; import { vertexAI } from '@genkit-ai/vertexai'; @@ -43,6 +42,5 @@ pinecone; chroma; devLocalVectorstore; genkitEval; -firebaseAuth; export const ai = genkit({}); diff --git a/js/testapps/firebase-functions-sample1/functions/package.json b/js/testapps/firebase-functions-sample1/functions/package.json index 9407d1c84..568eecae6 100644 --- a/js/testapps/firebase-functions-sample1/functions/package.json +++ b/js/testapps/firebase-functions-sample1/functions/package.json @@ -14,11 +14,11 @@ }, "main": "lib/index.js", "dependencies": { - "genkit": "*", - "@genkit-ai/firebase": "*", - "@genkit-ai/vertexai": "*", - "firebase-admin": ">=12.2", - "firebase-functions": ">=4.8" + "genkit": "^1.0.0-rc.14", + "@genkit-ai/firebase": "^1.0.0-rc.14", + "@genkit-ai/vertexai": "^1.0.0-rc.14", + "firebase-admin": "^12.2", + "firebase-functions": "^6.3.1" }, "devDependencies": { "firebase-functions-test": "^3.1.0", diff --git a/js/testapps/firebase-functions-sample1/functions/src/index.ts b/js/testapps/firebase-functions-sample1/functions/src/index.ts index 576d7ae88..4fe1fabf8 100644 --- a/js/testapps/firebase-functions-sample1/functions/src/index.ts +++ b/js/testapps/firebase-functions-sample1/functions/src/index.ts @@ -14,66 +14,48 @@ * limitations under the License. */ -import { firebase } from '@genkit-ai/firebase'; -import { firebaseAuth } from '@genkit-ai/firebase/auth'; -import { noAuth, onFlow } from '@genkit-ai/firebase/functions'; +import { enableFirebaseTelemetry } from '@genkit-ai/firebase'; import { FirebaseUserEngagementSchema, collectUserEngagement, } from '@genkit-ai/firebase/user_engagement'; -import { geminiPro, vertexAI } from '@genkit-ai/vertexai'; -import { onRequest } from 'firebase-functions/v2/https'; -import { genkit, run, z } from 'genkit'; +import { gemini15Flash, vertexAI } from '@genkit-ai/vertexai'; +import { onCallGenkit, onRequest } from 'firebase-functions/https'; +import { genkit, z } from 'genkit'; + +enableFirebaseTelemetry(); const ai = genkit({ - plugins: [firebase(), vertexAI()], - flowStateStore: 'firebase', - traceStore: 'firebase', - enableTracingAndMetrics: true, - logLevel: 'debug', - telemetry: { - instrumentation: 'firebase', - logger: 'firebase', - }, + plugins: [vertexAI()], }); -export const simpleFlow = onFlow( - ai, +export const simpleFlow = ai.defineFlow( { name: 'simpleFlow', inputSchema: z.string(), outputSchema: z.string(), - httpsOptions: { - cors: '*', - }, - authPolicy: noAuth(), }, async (subject) => { return 'hello world!'; } ); -export const jokeFlow = onFlow( - ai, +// No access to secrets (e.g. only works because simpleFlow does not call +// ai.generate). No authPolicy or App Check integration; +export const simple = onCallGenkit(simpleFlow); + +const jokeFlow = ai.defineFlow( { name: 'jokeFlow', inputSchema: z.string(), outputSchema: z.string(), - httpsOptions: { - cors: '*', - }, - authPolicy: firebaseAuth((user) => { - if (!user.email_verified && !user.admin) { - throw new Error('Email not verified'); - } - }), }, async (subject) => { const prompt = `Tell me a joke about ${subject}`; - return await run('call-llm', async () => { + return await ai.run('call-llm', async () => { const llmResponse = await ai.generate({ - model: geminiPro, + model: gemini15Flash, prompt: prompt, }); @@ -82,69 +64,90 @@ export const jokeFlow = onFlow( } ); -export const authFlow = onFlow( - ai, +export const joke = onCallGenkit( + { + secrets: ['apiKey'], + authPolicy: (auth) => auth?.token?.email_verified && auth?.token?.admin, + }, + jokeFlow +); + +const authFlow = ai.defineFlow( { name: 'authFlow', inputSchema: z.object({ uid: z.string(), input: z.string() }), outputSchema: z.string(), - authPolicy: firebaseAuth((user, input) => { - if (user.user_id !== input.uid) { - throw new Error('User must act as themselves'); - } - }), }, async ({ input }) => input.toUpperCase() ); -export const streamer = onFlow( - ai, +export const auth = onCallGenkit( + { + authPolicy: (auth, input) => !!auth && auth.uid === input.uid, + }, + authFlow +); + +const streamingFlow = ai.defineFlow( { name: 'streamer', inputSchema: z.number(), outputSchema: z.string(), streamSchema: z.object({ count: z.number() }), - httpsOptions: { invoker: 'private' }, - authPolicy: noAuth(), }, - async (count, streamingCallback) => { - console.log('streamingCallback', !!streamingCallback); + async (count, { sendChunk }) => { let i = 0; - if (streamingCallback) { - for (; i < count; i++) { - await new Promise((r) => setTimeout(r, 1000)); - streamingCallback({ count: i }); - } + for (; i < count; i++) { + await new Promise((r) => setTimeout(r, 1000)); + sendChunk({ count: i }); } return `done: ${count}, streamed: ${i} times`; } ); -export const streamConsumer = onFlow( - ai, +// onCallFlow automatically handles streaming when passed +// a flow that streams. This example sets invoker to "private", +// which means that IAM enforces access. +export const streamer = onCallGenkit( { - name: 'streamConsumer', - httpsOptions: { invoker: 'private' }, - authPolicy: noAuth(), + invoker: 'private', + }, + streamingFlow +); + +export const streamConsumerFlow = ai.defineFlow( + { + name: 'streamConsumerFlow', }, async () => { - const response = streamer(5); + const { output, stream } = streamingFlow.stream(5); - for await (const chunk of response.stream) { - console.log('chunk', chunk); + for await (const chunk of stream) { + console.log(`chunk ${chunk}`); } - console.log('streamConsumer done', await response.output); + console.log(`Streaming consumer done ${await output}`); } ); +export const streamConsumer = onCallGenkit( + { + invoker: 'private', + }, + streamConsumerFlow +); + export const triggerJokeFlow = onRequest( - { invoker: 'private' }, + { + invoker: 'private', + }, async (req, res) => { const { subject } = req.query; console.log('req.query', req.query); - const op = await jokeFlow(String(subject), { - withLocalAuthContext: { admin: true }, + const op = await jokeFlow.run(String(subject), { + context: { + auth: { admin: true }, + }, }); console.log('operation', op); res.send(op); @@ -153,7 +156,9 @@ export const triggerJokeFlow = onRequest( /** Example of user engagement collection using Firebase Functions. */ export const collectEngagement = onRequest( - { memory: '512MiB' }, + { + memory: '512MiB', + }, async (req, res) => { await collectUserEngagement(FirebaseUserEngagementSchema.parse(req.body)); res.send({}); From 5e6a61b1fe7def609934fdd1857fff4c9fded1b4 Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Tue, 4 Feb 2025 19:17:53 +0000 Subject: [PATCH 532/562] fix: update auth to context (#1822) --- docs/auth.md | 6 ++--- docs/evaluation.md | 5 ++--- genkit-tools/cli/src/commands/eval-flow.ts | 10 +++------ .../cli/src/commands/flow-batch-run.ts | 10 +++------ genkit-tools/cli/src/commands/flow-run.ts | 10 +++------ genkit-tools/common/src/eval/evaluate.ts | 22 ++++++++++--------- genkit-tools/common/src/types/apis.ts | 2 +- 7 files changed, 27 insertions(+), 38 deletions(-) diff --git a/docs/auth.md b/docs/auth.md index 5ba3b4ebf..56de189dd 100644 --- a/docs/auth.md +++ b/docs/auth.md @@ -103,10 +103,10 @@ export const selfSummaryFlow = ai.defineFlow( ``` When testing flows with Genkit dev tools, you are able to specify this auth -object in the UI, or on the command line with the `--auth` flag: +object in the UI, or on the command line with the `--context` flag: ```posix-terminal -genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --auth '{"uid": "abc-def"}' +genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --context '{"auth": {"email_verified": true}}' ``` ## Cloud Functions for Firebase integration @@ -153,7 +153,7 @@ above. When running this flow during development, you would pass the user object in the same way: ```posix-terminal -genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --auth '{"admin": true}' +genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --context '{"auth": {"admin": true}}' ``` Whenever you expose a Cloud Function to the wider internet, it is vitally diff --git a/docs/evaluation.md b/docs/evaluation.md index 385a6da04..379aba293 100644 --- a/docs/evaluation.md +++ b/docs/evaluation.md @@ -87,7 +87,6 @@ export const qaFlow = ai.defineFlow({ const factDocs = await ai.retrieve({ retriever: dummyRetriever, query, - options: { k: 2 }, }); const llmResponse = await ai.generate({ @@ -306,10 +305,10 @@ field and an optional `reference` field, like below: ] ``` -If your flow requires auth, you may specify it using the `--auth` argument: +If your flow requires auth, you may specify it using the `--context` argument: ```posix-terminal -genkit eval:flow qaFlow --input testInputs.json --auth "{\"email_verified\": true}" +genkit eval:flow qaFlow --input testInputs.json --context '{"auth": {"email_verified": true}}' ``` By default, the `eval:flow` and `eval:run` commands use all available metrics diff --git a/genkit-tools/cli/src/commands/eval-flow.ts b/genkit-tools/cli/src/commands/eval-flow.ts index 68eb8a38a..10ea8fcc2 100644 --- a/genkit-tools/cli/src/commands/eval-flow.ts +++ b/genkit-tools/cli/src/commands/eval-flow.ts @@ -41,7 +41,7 @@ import { runWithManager } from '../utils/manager-utils'; interface EvalFlowRunCliOptions { input?: string; output?: string; - auth?: string; + context?: string; evaluators?: string; force?: boolean; outputFormat: string; @@ -65,11 +65,7 @@ export const evalFlow = new Command('eval:flow') '--input ', 'Input dataset ID or JSON file to be used for evaluation' ) - .option( - '-a, --auth ', - 'JSON object passed to authPolicy and stored in local state as auth', - '' - ) + .option('-c, --context ', 'JSON object passed to context', '') .option( '-o, --output ', 'Name of the output file to write evaluation results. Defaults to json output.' @@ -149,7 +145,7 @@ export const evalFlow = new Command('eval:flow') manager, actionRef, inferenceDataset, - auth: options.auth, + context: options.context, }); const evalRun = await runEvaluation({ diff --git a/genkit-tools/cli/src/commands/flow-batch-run.ts b/genkit-tools/cli/src/commands/flow-batch-run.ts index 269040806..6d4c34fbe 100644 --- a/genkit-tools/cli/src/commands/flow-batch-run.ts +++ b/genkit-tools/cli/src/commands/flow-batch-run.ts @@ -23,7 +23,7 @@ interface FlowBatchRunOptions { wait?: boolean; output?: string; label?: string; - auth?: string; + context?: string; } /** Command to run flows with batch input. */ @@ -34,11 +34,7 @@ export const flowBatchRun = new Command('flow:batchRun') .argument('', 'name of the flow to run') .argument('', 'JSON batch data to use to run the flow') .option('-w, --wait', 'Wait for the flow to complete', false) - .option( - '-a, --auth ', - 'JSON object passed to authPolicy and stored in local state as auth', - '' - ) + .option('-c, --context ', 'JSON object passed to context', '') .option('--output ', 'name of the output file to store the output') .option('--label [label]', 'label flow run in this batch') .action( @@ -64,7 +60,7 @@ export const flowBatchRun = new Command('flow:batchRun') let response = await manager.runAction({ key: `/flow/${flowName}`, input: data, - context: options.auth ? JSON.parse(options.auth) : undefined, + context: options.context ? JSON.parse(options.context) : undefined, telemetryLabels: options.label ? { batchRun: options.label } : undefined, diff --git a/genkit-tools/cli/src/commands/flow-run.ts b/genkit-tools/cli/src/commands/flow-run.ts index f7954f0a7..42b9b9e88 100644 --- a/genkit-tools/cli/src/commands/flow-run.ts +++ b/genkit-tools/cli/src/commands/flow-run.ts @@ -23,7 +23,7 @@ interface FlowRunOptions { wait?: boolean; output?: string; stream?: boolean; - auth?: string; + context?: string; } /** Command to run a flow. */ @@ -33,11 +33,7 @@ export const flowRun = new Command('flow:run') .argument('[data]', 'JSON data to use to start the flow') .option('-w, --wait', 'Wait for the flow to complete', false) .option('-s, --stream', 'Stream output', false) - .option( - '-a, --auth ', - 'JSON object passed to authPolicy and stored in local state as auth', - '' - ) + .option('-c, --context ', 'JSON object passed to context', '') .option( '--output ', 'name of the output file to store the extracted data' @@ -50,7 +46,7 @@ export const flowRun = new Command('flow:run') { key: `/flow/${flowName}`, input: data ? JSON.parse(data) : undefined, - context: options.auth ? JSON.parse(options.auth) : undefined, + context: options.context ? JSON.parse(options.context) : undefined, }, options.stream ? (chunk) => console.log(JSON.stringify(chunk, undefined, ' ')) diff --git a/genkit-tools/common/src/eval/evaluate.ts b/genkit-tools/common/src/eval/evaluate.ts index eeecff529..1f9ab3158 100644 --- a/genkit-tools/common/src/eval/evaluate.ts +++ b/genkit-tools/common/src/eval/evaluate.ts @@ -110,7 +110,7 @@ export async function runNewEvaluation( manager, actionRef, inferenceDataset, - auth: request.options?.auth, + context: request.options?.context, actionConfig: request.options?.actionConfig, }); const evaluatorActions = await getMatchingEvaluatorActions( @@ -136,10 +136,11 @@ export async function runInference(params: { manager: RuntimeManager; actionRef: string; inferenceDataset: Dataset; - auth?: string; + context?: string; actionConfig?: any; }): Promise { - const { manager, actionRef, inferenceDataset, auth, actionConfig } = params; + const { manager, actionRef, inferenceDataset, context, actionConfig } = + params; if (!isSupportedActionRef(actionRef)) { throw new Error('Inference is only supported on flows and models'); } @@ -148,7 +149,7 @@ export async function runInference(params: { manager, actionRef, inferenceDataset, - auth, + context, actionConfig, }); return evalDataset; @@ -235,10 +236,11 @@ async function bulkRunAction(params: { manager: RuntimeManager; actionRef: string; inferenceDataset: Dataset; - auth?: string; + context?: string; actionConfig?: any; }): Promise { - const { manager, actionRef, inferenceDataset, auth, actionConfig } = params; + const { manager, actionRef, inferenceDataset, context, actionConfig } = + params; const isModelAction = actionRef.startsWith('/model'); if (inferenceDataset.length === 0) { throw new Error('Cannot run inference, no data provided'); @@ -268,7 +270,7 @@ async function bulkRunAction(params: { manager, actionRef, sample, - auth, + context, }) ); } @@ -285,15 +287,15 @@ async function runFlowAction(params: { manager: RuntimeManager; actionRef: string; sample: FullInferenceSample; - auth?: any; + context?: any; }): Promise { - const { manager, actionRef, sample, auth } = { ...params }; + const { manager, actionRef, sample, context } = { ...params }; let state: InferenceRunState; try { const runActionResponse = await manager.runAction({ key: actionRef, input: sample.input, - context: auth ? JSON.parse(auth) : undefined, + context: context ? JSON.parse(context) : undefined, }); state = { ...sample, diff --git a/genkit-tools/common/src/types/apis.ts b/genkit-tools/common/src/types/apis.ts index 5ae804549..735262990 100644 --- a/genkit-tools/common/src/types/apis.ts +++ b/genkit-tools/common/src/types/apis.ts @@ -139,7 +139,7 @@ export const RunNewEvaluationRequestSchema = z.object({ evaluators: z.array(z.string()).optional(), options: z .object({ - auth: z.string().optional(), + context: z.string().optional(), actionConfig: z .any() .describe('addition parameters required for inference') From 56824c992565c4c1ae29269eb50c3f9fb532a7f0 Mon Sep 17 00:00:00 2001 From: Alex Pascal Date: Tue, 4 Feb 2025 11:43:02 -0800 Subject: [PATCH 533/562] Update devtools.md to reference an existing runtime running. (#1831) --- docs/devtools.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devtools.md b/docs/devtools.md index 609b483af..8425c315a 100644 --- a/docs/devtools.md +++ b/docs/devtools.md @@ -16,8 +16,8 @@ npm install -D genkit-cli The CLI supports various commands to facilitate working with Genkit projects: - `genkit start -- `: Start the developer UI and connect it to a running code process. -- `genkit flow:run `: Run a specified flow. -- `genkit eval:flow `: Evaluate a specific flow. +- `genkit flow:run `: Run a specified flow. Your runtime must already be running in a separate terminal with `GENKIT_ENV=dev` environment variable set. +- `genkit eval:flow `: Evaluate a specific flow. Your runtime must already be running in a separate terminal with `GENKIT_ENV=dev` environment variable set. For a full list of commands, use: From 123f0115b02a5090c1fff60610fe84f99d5c33b5 Mon Sep 17 00:00:00 2001 From: Alex Pascal Date: Tue, 4 Feb 2025 11:43:30 -0800 Subject: [PATCH 534/562] Update chat.md to beta. (#1830) --- docs/chat.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/chat.md b/docs/chat.md index 4d1417293..a8288d66b 100644 --- a/docs/chat.md +++ b/docs/chat.md @@ -19,12 +19,15 @@ If you want to run the code examples on this page, first complete the steps in the [Getting started](get-started) guide. All of the examples assume that you have already installed Genkit as a dependency in your project. +Please note that the chat API is currently in beta and must be used from the +`genkit/beta` package. + ## Chat session basics Here is a minimal, console-based, chatbot application: ```ts -import { genkit } from "genkit"; +import { genkit } from "genkit/beta"; import { googleAI, gemini15Flash } from "@genkit-ai/googleai"; import { createInterface } from "node:readline/promises"; From 4f09d780c0ca49fa4141c4db2732932718de3d17 Mon Sep 17 00:00:00 2001 From: Anthony Barone Date: Tue, 4 Feb 2025 14:43:48 -0500 Subject: [PATCH 535/562] chore: update pnpm to v9.15.5 (#1825) --- genkit-tools/package.json | 2 +- js/package.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/genkit-tools/package.json b/genkit-tools/package.json index 10dfc1367..8cd1761a5 100644 --- a/genkit-tools/package.json +++ b/genkit-tools/package.json @@ -25,5 +25,5 @@ "cross-spawn": "^7.0.5" } }, - "packageManager": "pnpm@9.15.1+sha256.9e534e70afef06374f6126b44bda5760947135ce16a30aef1010e965fb7e3e3e" + "packageManager": "pnpm@9.15.5+sha256.8472168c3e1fd0bff287e694b053fccbbf20579a3ff9526b6333beab8df65a8d" } diff --git a/js/package.json b/js/package.json index 7e9676ba5..8c636d5c3 100644 --- a/js/package.json +++ b/js/package.json @@ -34,5 +34,5 @@ "cross-spawn": "^7.0.5" } }, - "packageManager": "pnpm@9.15.1+sha256.9e534e70afef06374f6126b44bda5760947135ce16a30aef1010e965fb7e3e3e" + "packageManager": "pnpm@9.15.5+sha256.8472168c3e1fd0bff287e694b053fccbbf20579a3ff9526b6333beab8df65a8d" } diff --git a/package.json b/package.json index f509a80eb..445282a4a 100644 --- a/package.json +++ b/package.json @@ -44,5 +44,5 @@ "cross-spawn": "^7.0.5" } }, - "packageManager": "pnpm@9.15.1+sha256.9e534e70afef06374f6126b44bda5760947135ce16a30aef1010e965fb7e3e3e" + "packageManager": "pnpm@9.15.5+sha256.8472168c3e1fd0bff287e694b053fccbbf20579a3ff9526b6333beab8df65a8d" } From dcacadcd0f6717df51bc6ff7a24f9487c090251d Mon Sep 17 00:00:00 2001 From: shrutip90 Date: Tue, 4 Feb 2025 11:48:35 -0800 Subject: [PATCH 536/562] fix: dotprompt docs (#1833) --- docs/dotprompt.md | 4 ++-- js/doc-snippets/src/dotprompt/index.ts | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/dotprompt.md b/docs/dotprompt.md index 0e45a6250..f7884779b 100644 --- a/docs/dotprompt.md +++ b/docs/dotprompt.md @@ -44,7 +44,7 @@ Before reading this page, you should be familiar with the content covered on the [Generating content with AI models](models) page. If you want to run the code examples on this page, first complete the steps in -the [Getting started](get-started) guide. All of the examples assume that you +the [Get started](get-started) guide. All of the examples assume that you have already installed Genkit as a dependency in your project. ## Creating prompt files @@ -364,7 +364,7 @@ your prompt's input schema. You already saw this in action in the section on input and output schemas: ```handlebars -{% includecode github_path="firebase/genkit/js/doc-snippets/src/dotprompt/prompts/ex03.prompt" %} +{% includecode github_path="firebase/genkit/js/doc-snippets/src/dotprompt/prompts/ex04.prompt" %} ``` In this example, the Handlebars expression, `{{theme}}`, diff --git a/js/doc-snippets/src/dotprompt/index.ts b/js/doc-snippets/src/dotprompt/index.ts index 5a4ac9e94..18c184f22 100644 --- a/js/doc-snippets/src/dotprompt/index.ts +++ b/js/doc-snippets/src/dotprompt/index.ts @@ -92,11 +92,11 @@ async function fn04() { // [START outSchema] // [START inSchema] const menuPrompt = ai.prompt('menu'); - const { data } = await menuPrompt({ theme: 'medieval' }); + const { output } = await menuPrompt({ theme: 'medieval' }); // [END inSchema] - const dishName = data['dishname']; - const description = data['description']; + const dishName = output['dishname']; + const description = output['description']; // [END outSchema] } @@ -107,11 +107,11 @@ async function fn05() { typeof MenuItemSchema, // Output schema z.ZodTypeAny // Custom options schema >('menu'); - const { data } = await menuPrompt({ theme: 'medieval' }); + const { output } = await menuPrompt({ theme: 'medieval' }); // Now data is strongly typed as MenuItemSchema: - const dishName = data?.dishname; - const description = data?.description; + const dishName = output?.dishname; + const description = output?.description; // [END outSchema2] } From ed8439bd6515653bf689b529119e46126869a8ae Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Tue, 4 Feb 2025 19:55:26 +0000 Subject: [PATCH 537/562] fix: eval plugin docs (#1814) --- docs/evaluation.md | 30 ++++++++++++-------------- docs/plugin-authoring-evaluator.md | 34 ++++++++++++++---------------- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/docs/evaluation.md b/docs/evaluation.md index 379aba293..e16443553 100644 --- a/docs/evaluation.md +++ b/docs/evaluation.md @@ -42,8 +42,8 @@ This section explains how to perform inference-based evaluation using Genkit. ### Setup
    -
  1. Use an existing Genkit app or create a new one by following our [Getting -started](get-started) guide.
  2. +
  3. Use an existing Genkit app or create a new one by following our [Get +started](get-started.md) guide.
  4. Add the following code to define a simple RAG application to evaluate. For this guide, we use a dummy retriever that always returns the same documents. @@ -52,7 +52,6 @@ import { genkit, z, Document } from "genkit"; import { googleAI, gemini15Flash, - gemini15Pro, } from "@genkit-ai/googleai"; // Initialize Genkit @@ -163,7 +162,7 @@ to open the Datasets page. c. Repeat steps (a) and (b) a couple more times to add more examples. This guide adds the following example inputs to the dataset: - ``` + ```none {:.devsite-disable-click-to-copy} "Can I give milk to my cats?" "From which animals did dogs evolve?" ``` @@ -173,8 +172,8 @@ to open the Datasets page. ### Run evaluation and view results -To start evaluating the flow, click the `Evaluations` tab in the Dev UI and -click the **Run new evaluation** button to get started. +To start evaluating the flow, click the **Run new evaluation** button on your +dataset page. You can also start a new evaluation from the `Evaluations` tab. 1. Select the `Flow` radio button to evaluate a flow. @@ -233,7 +232,7 @@ and is only enforced if a schema is specified on the target flow. control for advanced use cases (e.g. providing model parameters, message history, tools, etc). You can find the full schema for `GenerateRequest` in our [API reference - docs](https://js.api.genkit.dev/interfaces/genkit._.GenerateRequest.html). + docs](https://js.api.genkit.dev/interfaces/genkit._.GenerateRequest.html){: .external}. Note: Schema validation is a helper tool for editing examples, but it is possible to save an example with invalid schema. These examples may fail when @@ -244,7 +243,7 @@ the running an evaluation. ### Genkit evaluators Genkit includes a small number of native evaluators, inspired by -[RAGAS](https://docs.ragas.io/en/stable/), to help you get started: +[RAGAS](https://docs.ragas.io/en/stable/){: .external}, to help you get started: * Faithfulness -- Measures the factual consistency of the generated answer against the given context @@ -256,7 +255,7 @@ harm, or exploit ### Evaluator plugins Genkit supports additional evaluators through plugins, like the Vertex Rapid -Evaluators, which you access via the [VertexAI +Evaluators, which you can access via the [VertexAI Plugin](./plugins/vertex-ai#evaluators). ## Advanced use @@ -316,7 +315,7 @@ for evaluation. To run on a subset of the configured evaluators, use the `--evaluators` flag and provide a comma-separated list of evaluators by name: ```posix-terminal -genkit eval:flow qaFlow --input testInputs.json --evaluators=genkit/faithfulness,genkit/answer_relevancy +genkit eval:flow qaFlow --input testInputs.json --evaluators=genkitEval/maliciousness,genkitEval/answer_relevancy ``` You can view the results of your evaluation run in the Dev UI at `localhost:4000/evaluate`. @@ -393,9 +392,8 @@ export const qaFlow = ai.defineFlow({ const factDocs = await ai.retrieve({ retriever: dummyRetriever, query, - options: { k: 2 }, }); - const factDocsModified = await run('factModified', async () => { + const factDocsModified = await ai.run('factModified', async () => { // Let us use only facts that are considered silly. This is a // hypothetical step for demo purposes, you may perform any // arbitrary task inside a step and reference it in custom @@ -408,7 +406,7 @@ export const qaFlow = ai.defineFlow({ const llmResponse = await ai.generate({ model: gemini15Flash, prompt: `Answer this question with the given context ${query}`, - docs: factDocs, + docs: factDocsModified, }); return llmResponse.text; } @@ -482,7 +480,7 @@ Here is an example flow that uses a PDF file to generate potential user questions. ```ts -import { genkit, run, z } from "genkit"; +import { genkit, z } from "genkit"; import { googleAI, gemini15Flash } from "@genkit-ai/googleai"; import { chunk } from "llm-chunk"; // npm i llm-chunk import path from "path"; @@ -515,9 +513,9 @@ export const synthesizeQuestions = ai.defineFlow( async (filePath) => { filePath = path.resolve(filePath); // `extractText` loads the PDF and extracts its contents as text. - const pdfTxt = await run("extract-text", () => extractText(filePath)); + const pdfTxt = await ai.run("extract-text", () => extractText(filePath)); - const chunks = await run("chunk-it", async () => + const chunks = await ai.run("chunk-it", async () => chunk(pdfTxt, chunkingConfig) ); diff --git a/docs/plugin-authoring-evaluator.md b/docs/plugin-authoring-evaluator.md index 6b8c58172..9ee8d218b 100644 --- a/docs/plugin-authoring-evaluator.md +++ b/docs/plugin-authoring-evaluator.md @@ -61,23 +61,22 @@ function getDeliciousnessPrompt(ai: Genkit) { output: { schema: DeliciousnessDetectionResponseSchema, } - }, - `You are a food critic. Assess whether the provided output sounds delicious, giving only "yes" (delicious), "no" (not delicious), or "maybe" (undecided) as the verdict. + prompt: `You are a food critic. Assess whether the provided output sounds delicious, giving only "yes" (delicious), "no" (not delicious), or "maybe" (undecided) as the verdict. - Examples: - Output: Chicken parm sandwich - Response: { "reason": "A classic and beloved dish.", "verdict": "yes" } + Examples: + Output: Chicken parm sandwich + Response: { "reason": "A classic and beloved dish.", "verdict": "yes" } - Output: Boston Logan Airport tarmac - Response: { "reason": "Not edible.", "verdict": "no" } + Output: Boston Logan Airport tarmac + Response: { "reason": "Not edible.", "verdict": "no" } - Output: A juicy piece of gossip - Response: { "reason": "Metaphorically 'tasty' but not food.", "verdict": "maybe" } + Output: A juicy piece of gossip + Response: { "reason": "Metaphorically 'tasty' but not food.", "verdict": "maybe" } - New Output: {% verbatim %}{{ responseToTest }} {% endverbatim %} - Response: - ` - ); + New Output: {% verbatim %}{{ responseToTest }} {% endverbatim %} + Response: + ` + }); } ``` @@ -91,7 +90,7 @@ responsibility of the evaluator to validate that all fields required for evaluation are present. ```ts -import { ModelArgument, z } from 'genkit'; +import { ModelArgument } from 'genkit'; import { BaseEvalDataPoint, Score } from 'genkit/evaluator'; /** @@ -100,6 +99,7 @@ import { BaseEvalDataPoint, Score } from 'genkit/evaluator'; export async function deliciousnessScore< CustomModelOptions extends z.ZodTypeAny, >( + ai: Genkit, judgeLlm: ModelArgument, dataPoint: BaseEvalDataPoint, judgeConfig?: CustomModelOptions @@ -141,8 +141,7 @@ export async function deliciousnessScore< The final step is to write a function that defines the `EvaluatorAction`. ```ts -import { Genkit, z } from 'genkit'; -import { BaseEvalDataPoint, EvaluatorAction } from 'genkit/evaluator'; +import { EvaluatorAction } from 'genkit/evaluator'; /** * Create the Deliciousness evaluator action. @@ -162,7 +161,7 @@ export function createDeliciousnessEvaluator< isBilled: true, }, async (datapoint: BaseEvalDataPoint) => { - const score = await deliciousnessScore(judge, datapoint, judgeConfig); + const score = await deliciousnessScore(ai, judge, datapoint, judgeConfig); return { testCaseId: datapoint.testCaseId, evaluation: score, @@ -245,7 +244,6 @@ As with the LLM-based evaluator, define the scoring function. In this case, the scoring function does not need a judge LLM. ```ts -import { EvalResponses } from 'genkit'; import { BaseEvalDataPoint, Score } from 'genkit/evaluator'; const US_PHONE_REGEX = From 0da06707edef9de1e5c57653bfa98f3206c13dbc Mon Sep 17 00:00:00 2001 From: ssbushi <66321939+ssbushi@users.noreply.github.com> Date: Tue, 4 Feb 2025 20:35:23 +0000 Subject: [PATCH 538/562] fix: syntax in pgvector (#1837) --- docs/templates/pgvector.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/templates/pgvector.md b/docs/templates/pgvector.md index 4c8e3593a..55551bdfc 100644 --- a/docs/templates/pgvector.md +++ b/docs/templates/pgvector.md @@ -5,7 +5,7 @@ following example as a starting point and modify it to work with your database schema. ```ts -import { genkit, z } from 'genkit'; +import { genkit, z, Document } from 'genkit'; import { googleAI, textEmbedding004 } from '@genkit-ai/googleai'; import { toSql } from 'pgvector'; import postgres from 'postgres'; @@ -40,7 +40,7 @@ const sqlRetriever = ai.defineRetriever( return { documents: results.map((row) => { const { content, ...metadata } = row; - return ai.Document.fromText(content, metadata); + return Document.fromText(content, metadata); }), }; } From 0021150b03b2a242617741351019f0b752eb3f76 Mon Sep 17 00:00:00 2001 From: thedmail Date: Tue, 4 Feb 2025 12:41:01 -0800 Subject: [PATCH 539/562] docs: update tool-calling.md (#1834) --- docs/tool-calling.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/tool-calling.md b/docs/tool-calling.md index 97382c1e8..3fbef9b27 100644 --- a/docs/tool-calling.md +++ b/docs/tool-calling.md @@ -147,7 +147,7 @@ Include defined tools in your prompts to generate content. name: 'weatherPrompt', tools: [getWeather], }, - 'What is the weather in {{location}}?' + 'What is the weather in {% verbatim %}{{location}}{% endverbatim %}?' ); const response = await weatherPrompt({ location: 'Baltimore' }); @@ -164,7 +164,7 @@ Include defined tools in your prompts to generate content. location: string --- - What is the weather in {{location}}? + What is the weather in {% verbatim %}{{location}}{% endverbatim %}? ``` Then you can execute the prompt in your code as follows: @@ -196,14 +196,15 @@ Include defined tools in your prompts to generate content. Genkit will automatically handle the tool call if the LLM needs to use the `getWeather` tool to answer the prompt. -### Pausing the tool loop with interrupts +### Pause the tool loop by using interrupts By default, Genkit repeatedly calls the LLM until every tool call has been -resolved. In some situations you may wish to conditionally pause execution to: +resolved. You may want to conditionally pause execution in situations where +you want to, for example: -- ask the user a question or display UI -- confirm a potentially risky action with the user -- request out-of-band approval for an action +* Ask the user a question or display UI. +* Confirm a potentially risky action with the user. +* Request out-of-band approval for an action. **Interrupts** are special tools that can halt the loop and return control to your code so that you can handle more advanced scenarios. Visit the @@ -211,7 +212,7 @@ to your code so that you can handle more advanced scenarios. Visit the ### Explicitly handling tool calls -If you want full control over this tool calling loop, for example to +If you want full control over this tool-calling loop, for example to apply more complicated logic, set the `returnToolRequests` parameter to `true`. Now it's your responsibility to ensure all of the tool requests are fulfilled: From fe483474228d35b97f3cbe9dc8ef8c17580799a8 Mon Sep 17 00:00:00 2001 From: thedmail Date: Tue, 4 Feb 2025 12:41:08 -0800 Subject: [PATCH 540/562] docs: New page about interrupts (#1835) --- docs/interrupts.md | 235 ++++++++++++++++++++++++++++++++------------- 1 file changed, 169 insertions(+), 66 deletions(-) diff --git a/docs/interrupts.md b/docs/interrupts.md index 34536c8cf..a9a4413ce 100644 --- a/docs/interrupts.md +++ b/docs/interrupts.md @@ -1,61 +1,59 @@ -# Interrupts +# Pause generation using interrupts _Interrupts_ are a special kind of [tool](tool-calling) that can pause the -LLM generation and tool calling loop to return control back to you. When -you're ready, generation can then be *resumed* with *replies* that will be -processed by the LLM for further generation. +LLM generation-and-tool-calling loop to return control back to you. When +you're ready, you can then *resume* generation by sending *replies* that the LLM +processes for further generation. -The most common uses for interruptions fall into a few categories: +The most common uses for interrupts fall into a few categories: -* **Human-in-the-Loop:** Allowing the user of an interactive AI application - to clarify needed informatino or confirm the LLM's action before it is - completed, providing a measure of safety and confidence. +* **Human-in-the-Loop:** Enabling the user of an interactive AI + to clarify needed information or confirm the LLM's action + before it is completed, providing a measure of safety and confidence. * **Async Processing:** Starting an asynchronous task that can only be completed out-of-band, such as sending an approval notification to a human reviewer or kicking off a long-running background process. -* **Exiting Autonomous Task:** In workflows that might iterate through - a long series of tool calls, an interrupt can provide the model a way - to mark the task as complete. +* **Exit from an Autonomous Task:** Providing the model a way + to mark a task as complete, in a workflow that might iterate through + a long series of tool calls. ## Before you begin {:#you-begin} -If you want to run the code examples on this page, first complete the steps in -the [Getting started](get-started) guide. All of the examples assume that you -have already set up a project with Genkit dependencies installed. +All of the examples documented here assume that you have already set up a +project with Genkit dependencies installed. If you want to run the code +examples on this page, first complete the steps in the +[Get started](get-started) guide. -This page discusses one of the advanced features of Genkit model abstraction, so -before you dive too deeply, you should be familiar with the content on the -[Generating content with AI models](models) page. You should also be familiar -with Genkit's system for defining input and output schemas, which is discussed -on the [Flows](flows) page and the general methods of tool calling discussed -on the [Tool Calling](tool-calling) page. +Before diving too deeply, you should also be familiar with the following +concepts: + +* [Generating content](models) with AI models. +* Genkit's system for [defining input and output schemas](flows). +* General methods of [tool-calling](tool-calling). ## Overview of interrupts {:#overview-interrupts} -At a high level, this is what an interrupt looks like when interacting with an LLM: +At a high level, this is what an interrupt looks like when +interacting with an LLM: -1. The calling application prompts the LLM with a request and also includes in - the prompt a list of tools including at least one interrupt tool that the LLM +1. The calling application prompts the LLM with a request. The prompt includes + a list of tools, including at least one for an interrupt that the LLM can use to generate a response. -2. The LLM either generates a complete response or generates a tool call request - in a specific format. To the LLM, an interrupt looks like any other tool call. -3. If the LLM selects the interrupt among the tool calls it generates, the Genkit - library will automatically halt generation rather than immediately passing - responses back to the model for additional processing. -4. The developer checks if an interrupt is called and performs whatever task is - needed to collect the information needed for the interrupt reply. -5. The developer resumes generation by passing an interrupt reply to the model, - returning to Step 2. - -## Triggering interrupts with Genkit {:#tool-calling} - -Interrupts can be triggered from any tool or by using the `defineInterrupt` method. - -### Defining interrupts - -The most common kind of interrupt is providing a tool that allows the LLM to -request clarification from the user, for example by asking a multiple choice -question. +2. The LLM either generates either a complete response or a tool call request + in a specific format. To the LLM, an interrupt call looks like any + other tool call. +3. If the LLM calls an interrupt tool, + the Genkit library automatically pauses generation rather than immediately + passing responses back to the model for additional processing. +4. The developer checks whether an interrupt call is made, and performs whatever + task is needed to collect the information needed for the interrupt response. +5. The developer resumes generation by passing an interrupt response to the + model. This action triggers a return to Step 2. + +## Define manual-response interrupts {:#manual-response} + +The most common kind of interrupt allows the LLM to request clarification from +the user, for example by asking a multiple-choice question. For this use case, use the Genkit instance's `defineInterrupt()` method: @@ -75,20 +73,21 @@ const askQuestion = ai.defineInterrupt({ choices: z.array(z.string()).describe('the choices to display to the user'), allowOther: z.boolean().optional().describe('when true, allow write-ins') }), - replySchema: z.string() + outputSchema: z.string() }); ``` -Note that interrupts have a `replySchema` instead of an output schema, although -they are treated as equivalent when passing data to the model. +Note that the `outputSchema` of an interrupt corresponds to the response data +you will provide as opposed to something that will be automatically populated +by a tool function. -### Using interrupts +### Use interrupts Interrupts are passed into the `tools` array when generating content, just like -other types of tools. You can pass both normal tools and interrupts to the same -generate call: +other types of tools. You can pass both normal tools and interrupts to the +same `generate` call: -* {Generate} +* {generate} ```ts const response = await ai.generate({ @@ -107,7 +106,7 @@ generate call: input: { schema: z.object({subject: z.string()}) }, - prompt: 'Ask me a trivia question about {{subject}}.', + prompt: 'Ask me a trivia question about {% verbatim %}{{subject}}{% endverbatim %}.', } ); @@ -123,14 +122,14 @@ generate call: schema: partyType: string --- - {{role "system}} + {% verbatim %}{{role "system"}}{% endverbatim %} Use the askQuestion tool if you need to clarify something. - {{role "user"}} - Help me plan a {{partyType}} party next week. + {% verbatim %}{{role "user"}}{% endverbatim %} + Help me plan a {% verbatim %}{{partyType}}{% endverbatim %} party next week. ``` - Then you can execute the prompt in your code as follows: +Then you can execute the prompt in your code as follows: ```ts // assuming prompt file is named partyPlanner.prompt @@ -150,11 +149,11 @@ generate call: const response = await chat.send('make a plan for my birthday party'); ``` -Genkit will immediately return a response once an interrupt tool is triggered. +Genkit immediately returns a response on receipt of an interrupt tool call. -### Replying to interrupts +### Respond to interrupts -If you've passed one or more interrupt tools to your generate call, you will +If you've passed one or more interrupts to your generate call, you need to check the response for interrupts so that you can handle them: ```ts @@ -164,12 +163,12 @@ response.finishReason === 'interrupted' response.interrupts.length > 0 ``` -Replying to an interrupt is done using the `resume` option on a subsequent -generate call, making sure to pass in the existing history. Each tool has -a `.reply()` method on it to help construct the reply. +Responding to an interrupt is done using the `resume` option on a subsequent +`generate` call, making sure to pass in the existing history. Each tool has +a `.respond()` method on it to help construct the response. -Once resumed, the model will re-enter the generation loop including tool -execution until it either completes or another interrupt is triggered: +Once resumed, the model re-enters the generation loop, including tool +execution, until either it completes or another interrupt is triggered: ```ts let response = await ai.generate({ @@ -183,8 +182,8 @@ while (response.interrupts.length) { // multiple interrupts can be called at once, so we handle them all for (const question in response.interrupts) { answers.push( - // use the 'reply' method on our tool to populate answers - askQuestion.reply( + // use the `respond` method on our tool to populate answers + askQuestion.respond( question, // send the tool request input to the user to respond await askUser(question.toolRequest.input) @@ -196,11 +195,115 @@ while (response.interrupts.length) { tools: [askQuestion], messages: response.messages, resume: { - reply: answers + respond: answers } }) } // no more interrupts, we can see the final response console.log(response.text); -``` \ No newline at end of file +``` + +## Tools with restartable interrupts {:#restartable-interrupts} + +Another common pattern for interrupts is the need to *confirm* an action that +the LLM suggests before actually performing it. For example, a payments app +might want the user to confirm certain kinds of transfers. + +For this use case, you can use the standard `defineTool` method to add custom +logic around when to trigger an interrupt, and what to do when an interrupt is +*restarted* with additional metadata. + +### Define a restartable tool + +Every tool has access to two special helpers in the second argument of its +implementation definition: + +- `interrupt`: when called, this method throws a special kind of exception that + is caught to pause the generation loop. You can provide additional metadata + as an object. +- `resumed`: when a request from an interrupted generation is restarted using + the `{resume: {restart: ...}}` option (see below), this helper contains the + metadata provided when restarting. + +If you were building a payments app, for example, you might want to confirm with +the user before making a transfer exceeding a certain amount: + +```ts +const transferMoney = ai.defineTool({ + name: 'transferMoney', + description: 'Transfers money between accounts.', + inputSchema: z.object({ + toAccountId: z.string().describe('the account id of the transfer destination'), + amount: z.number().describe('the amount in integer cents (100 = $1.00)'), + }), + outputSchema: z.object({ + status: z.string().describe('the outcome of the transfer'), + message: z.string().optional(), + }) +}, async (input, {context, interrupt, resumed})) { + // if the user rejected the transaction + if (resumed?.status === "REJECTED") { + return {status: 'REJECTED', message: 'The user rejected the transaction.'}; + } + // trigger an interrupt to confirm if amount > $100 + if (resumed?.status !== "APPROVED" && input.amount > 10000) { + interrupt({ + message: "Please confirm sending an amount > $100.", + }); + } + // complete the transaction if not interrupted + return doTransfer(input); +} +``` + +In this example, on first execution (when `resumed` is undefined), the tool +checks to see if the amount exceeds $100, and triggers an interrupt if so. On +second execution, it looks for a status in the new metadata provided and +performs the transfer or returns a rejection response, depending on whether it +is approved or rejected. + +### Restart tools after interruption + +Interrupt tools give you full control over: + +1. When an initial tool request should trigger an interrupt. +2. When and whether to resume the generation loop. +3. What additional information to provide to the tool when resuming. + +In the example shown in the previous section, the application might ask the user +to confirm the interrupted request to make sure the transfer amount is okay: + +```ts +let response = await ai.generate({ + tools: [transferMoney], + prompt: "Transfer $1000 to account ABC123", +}); + +while (response.interrupts.length) { + const confirmations = []; + // multiple interrupts can be called at once, so we handle them all + for (const interrupt in response.interrupts) { + confirmations.push( + // use the 'restart' method on our tool to provide `resumed` metadata + transferMoney.restart( + interrupt, + // send the tool request input to the user to respond. assume that this + // returns `{status: "APPROVED"}` or `{status: "REJECTED"}` + await requestConfirmation(interrupt.toolRequest.input); + ) + ); + } + + response = await ai.generate({ + tools: [transferMoney], + messages: response.messages, + resume: { + restart: confirmations, + } + }) +} + +// no more interrupts, we can see the final response +console.log(response.text); +``` From c3775f9d7a4b01f1022be2486a2409716faf9e3e Mon Sep 17 00:00:00 2001 From: Michael Bleigh Date: Tue, 4 Feb 2025 12:41:43 -0800 Subject: [PATCH 541/562] chore(js): remove extraneous console.log statements (#1829) --- js/ai/src/generate/resolve-tool-requests.ts | 7 ++----- js/core/src/tracing/processor.ts | 4 ---- js/plugins/langchain/src/tracing.ts | 6 ------ js/plugins/mcp/src/server.ts | 1 - 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/js/ai/src/generate/resolve-tool-requests.ts b/js/ai/src/generate/resolve-tool-requests.ts index f87c6495b..3fb294f8e 100644 --- a/js/ai/src/generate/resolve-tool-requests.ts +++ b/js/ai/src/generate/resolve-tool-requests.ts @@ -337,7 +337,6 @@ export async function resolveResumeOption( interruptedResponse?: GenerateResponseData; }> { if (!rawRequest.resume) return { revisedRequest: rawRequest }; // no-op if no resume option - console.log('RESOLVE RESUME OPTION:', rawRequest.resume); const toolMap = toToolMap(await resolveTools(registry, rawRequest.tools)); const messages = rawRequest.messages; @@ -346,11 +345,11 @@ export async function resolveResumeOption( if ( !lastMessage || lastMessage.role !== 'model' || - !lastMessage.content.find((p) => p.toolRequest && p.metadata?.interrupt) + !lastMessage.content.find((p) => p.toolRequest) ) { throw new GenkitError({ status: 'FAILED_PRECONDITION', - message: `Cannot 'resume' generation unless the previous message is a model message with at least one interrupt.`, + message: `Cannot 'resume' generation unless the previous message is a model message with at least one tool request.`, }); } @@ -365,7 +364,6 @@ export async function resolveResumeOption( part, toolMap ); - console.log('RESOLVED TOOL', part.toolRequest.name, 'TO', resolved); if (resolved.interrupt) { interrupted = true; return resolved.interrupt; @@ -407,7 +405,6 @@ export async function resolveResumeOption( }, }; - console.log('CONSTRUCTED A TOOL MESSAGE:', toolMessage.content); return stripUndefinedProps({ revisedRequest: { ...rawRequest, diff --git a/js/core/src/tracing/processor.ts b/js/core/src/tracing/processor.ts index 73ccfebdb..6cb8c5752 100644 --- a/js/core/src/tracing/processor.ts +++ b/js/core/src/tracing/processor.ts @@ -72,10 +72,6 @@ class FilteringReadableSpanProxy implements ReadableSpan { return this.span.status; } get attributes() { - console.log( - 'FilteringReadableSpanProxy get attributes', - this.span.attributes - ); const out = {} as Record; for (const [key, value] of Object.entries(this.span.attributes)) { if (!key.startsWith(ATTR_PREFIX + ':')) { diff --git a/js/plugins/langchain/src/tracing.ts b/js/plugins/langchain/src/tracing.ts index 0c47cce76..1592e5e1e 100644 --- a/js/plugins/langchain/src/tracing.ts +++ b/js/plugins/langchain/src/tracing.ts @@ -62,13 +62,7 @@ export class GenkitTracer extends BaseTracer { ctx = trace.setSpan(context.active(), parentCtx); } const span = this.tracer.startSpan(run.name, undefined, ctx); - console.log('run', JSON.stringify(run, undefined, ' ')); if (run.inputs) { - console.log('setting inputs', run.inputs); - console.log( - 'setting inputs flattened', - this.maybeFlattenInput(run.inputs) - ); span.setAttribute( 'genkit:input', JSON.stringify(this.maybeFlattenInput(run.inputs)) diff --git a/js/plugins/mcp/src/server.ts b/js/plugins/mcp/src/server.ts index 5fdfd8fc9..55599f617 100644 --- a/js/plugins/mcp/src/server.ts +++ b/js/plugins/mcp/src/server.ts @@ -99,7 +99,6 @@ export class GenkitMcpServer { const toolList: ToolAction[] = []; const promptList: PromptAction[] = []; for (const k in allActions) { - console.log('action:', k); if (k.startsWith('/tool/')) { toolList.push(allActions[k] as ToolAction); } else if (k.startsWith('/prompt/')) { From 9ce522178cd0b97b826b8cc1bf03cb7499604331 Mon Sep 17 00:00:00 2001 From: Pavel Jbanov Date: Tue, 4 Feb 2025 15:48:55 -0500 Subject: [PATCH 542/562] docs: updated some docs (#1813) --- docs/cloud-run.md | 33 +++++++++++++++++++++------------ docs/plugin-authoring.md | 32 ++++++++++++-------------------- docs/tool-calling.md | 10 +++++----- 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/docs/cloud-run.md b/docs/cloud-run.md index e37b655c4..5551019a1 100644 --- a/docs/cloud-run.md +++ b/docs/cloud-run.md @@ -88,26 +88,32 @@ authorization: in the Cloud Run docs for information on providing these credentials. - **Authorization policy defined in code**: Use the authorization policy feature - of Genkit flows to verify authorization info using custom code. This is often, + of Genkit express plugin to verify authorization info using custom code. This is often, but not necessarily, token-based authorization. If you want to define an authorization policy in code, use the `authPolicy` parameter in the flow definition: ```ts -const myFlow = ai.defineFlow( - { - name: "myFlow", - authPolicy: (auth, input) => { - if (!auth) { - throw new Error("Authorization required."); +// middleware for handling auth tokens in headers. +const authMiddleware = async (req, resp, next) => { + // parse auth headers and convert to auth object. + (req as RequestWithAuth).auth = { + user: await verifyAuthToken(req.header('authorization')), + }; + next(); +}; + +app.post( + '/simpleFlow', + authMiddleware, + expressHandler(simpleFlow, { + authPolicy: ({ auth }) => { + if (!auth.user) { + throw new Error('not authorized'); } - // Custom checks go here... }, - }, - async () => { - // ... - } + }) ); ``` @@ -116,6 +122,9 @@ of the request object. You typically set this property using Express middleware. See [Authorization and integrity](/docs/genkit/auth#non-firebase_http_authorization). +Refer to [express plugin documentation](https://js.api.genkit.dev/modules/_genkit-ai_express.html) +for more details. + ### Make API credentials available to deployed flows Once deployed, your flows need some way to authenticate with any remote services diff --git a/docs/plugin-authoring.md b/docs/plugin-authoring.md index 3929b0994..dcd4d5fb7 100644 --- a/docs/plugin-authoring.md +++ b/docs/plugin-authoring.md @@ -64,9 +64,8 @@ requires a secret value, such as API keys, you should offer both an option and a default environment variable to configure it: ```ts -import { Genkit, z } from 'genkit'; +import { GenkitError, Genkit, z } from 'genkit'; import { GenkitPlugin, genkitPlugin } from 'genkit/plugin'; -import { GenkitError } from '@genkit-ai/core'; interface MyPluginOptions { apiKey?: string; @@ -107,23 +106,21 @@ A custom model generally consists of three components: 3. A function that implements the model accepting `GenerateRequest` and returning `GenerateResponse`. -To build a model plugin, you'll need to use the `@genkit-ai/ai` package: - -```posix-terminal -npm i --save @genkit-ai/ai -``` +To build a model plugin, you'll need to use the `genkit/model` package: At a high level, a model plugin might look something like this: ```ts import { genkitPlugin, GenkitPlugin } from 'genkit/plugin'; -import { GenkitError } from '@genkit-ai/core'; -import { GenerationCommonConfigSchema } from '@genkit-ai/ai/model'; -import { simulateSystemPrompt } from '@genkit-ai/ai/model/middleware'; -import { z } from 'genkit'; +import { GenerationCommonConfigSchema } from 'genkit/model'; +import { simulateSystemPrompt } from 'genkit/model/middleware'; +import { Genkit, GenkitError, z } from 'genkit'; +export interface MyPluginOptions { + // ... +} -export function myPlugin(options?: MyPluginOptions) { +export function myPlugin(options?: MyPluginOptions): GenkitPlugin { return genkitPlugin('my-plugin', async (ai: Genkit) => { ai.defineModel({ // be sure to include your plugin as a provider prefix @@ -153,8 +150,6 @@ export function myPlugin(options?: MyPluginOptions) { }); }); }; - - ``` #### Transforming Requests and Responses @@ -174,7 +169,7 @@ export a model reference from your package that includes only the metadata for a model but not its implementation: ```ts -import { modelRef } from "@genkit-ai/ai/model"; +import { modelRef } from "genkit/model"; export myModelRef = modelRef({ name: "my-plugin/my-model", @@ -189,11 +184,10 @@ When calling `generate()`, model references and string model names can be used i ```ts import { myModelRef } from 'genkitx-my-plugin'; -import { generate } from '@genkit-ai/ai'; -generate({ model: myModelRef }); +ai.generate({ model: myModelRef }); // is equivalent to -generate({ model: 'my-plugin/my-model' }); +ai.generate({ model: 'my-plugin/my-model' }); ``` ## Publishing a plugin @@ -209,8 +203,6 @@ plugin: - `genkit-retriever`: include this keyword if your package defines any retrievers. - `genkit-indexer`: include this keyword if your package defines any indexers. - `genkit-embedder`: include this keyword if your package defines any indexers. -- `genkit-tracestore`: include this keyword if your package defines any trace stores. -- `genkit-statestore`: include this keyword if your package defines any state stores. - `genkit-telemetry`: include this keyword if your package defines a telemetry provider. - `genkit-deploy`: include this keyword if your package includes helpers to deploy Genkit apps to cloud providers. - `genkit-flow`: include this keyword if your package enhances Genkit flows. diff --git a/docs/tool-calling.md b/docs/tool-calling.md index 3fbef9b27..ec7e3c259 100644 --- a/docs/tool-calling.md +++ b/docs/tool-calling.md @@ -120,11 +120,11 @@ const getWeather = ai.defineTool( ); ``` -The syntax here looks just like the `defineFlow()` syntax; however, all four of -the `name`, `description`, `inputSchema`, and `outputSchema` parameters are -required. When writing a tool definition, take special care with the wording and -descriptiveness of these parameters, as they are vital for the LLM to -effectively make use of the available tools. +The syntax here looks just like the `defineFlow()` syntax; however, `name`, +`description` and `inputSchema` parameters are required. When writing a tool +definition, take special care with the wording and descriptiveness of these +parameters, as they are vital for the LLM to effectively make use of the +available tools. ### Using tools From 6b716fc66bc8f3551545ac22fd63e90a353ea071 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:53:22 +0000 Subject: [PATCH 543/562] chore: bump @genkit-ai/tools-common version to @genkit-ai/tools-common@1.0.0-rc.16 --- genkit-tools/common/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/common/package.json b/genkit-tools/common/package.json index 02f5fac8f..735878c56 100644 --- a/genkit-tools/common/package.json +++ b/genkit-tools/common/package.json @@ -1,6 +1,6 @@ { "name": "@genkit-ai/tools-common", - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", "build:clean": "rimraf ./lib", From 4ea5824d3c92f183cba3335cddc333ccd8278344 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:53:24 +0000 Subject: [PATCH 544/562] chore: bump genkit-cli version to genkit-cli@1.0.0-rc.16 --- genkit-tools/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/cli/package.json b/genkit-tools/cli/package.json index a98d8ae0c..cb172d57e 100644 --- a/genkit-tools/cli/package.json +++ b/genkit-tools/cli/package.json @@ -1,6 +1,6 @@ { "name": "genkit-cli", - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "description": "CLI for interacting with the Google Genkit AI framework", "license": "Apache-2.0", "keywords": [ From ee49af7446ba727060a6d123e667e1ac95739441 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:53:26 +0000 Subject: [PATCH 545/562] chore: bump @genkit-ai/telemetry-server version to @genkit-ai/telemetry-server@1.0.0-rc.16 --- genkit-tools/telemetry-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/genkit-tools/telemetry-server/package.json b/genkit-tools/telemetry-server/package.json index 868a7df1b..bf120f755 100644 --- a/genkit-tools/telemetry-server/package.json +++ b/genkit-tools/telemetry-server/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "compile": "tsc -b ./tsconfig.cjs.json ./tsconfig.esm.json ./tsconfig.types.json", From 3d0dcb7e0477a3a83a9a07d284e6850f092ad82d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:03 +0000 Subject: [PATCH 546/562] chore: bump @genkit-ai/core version to @genkit-ai/core@1.0.0-rc.16 --- js/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/core/package.json b/js/core/package.json index 93df6def0..6dd8671fd 100644 --- a/js/core/package.json +++ b/js/core/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From 0c3eafa35b77194cc13f04cf1cbf74532dfab5e9 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:06 +0000 Subject: [PATCH 547/562] chore: bump @genkit-ai/ai version to @genkit-ai/ai@1.0.0-rc.16 --- js/ai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ai/package.json b/js/ai/package.json index de4228c70..52860d922 100644 --- a/js/ai/package.json +++ b/js/ai/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From 8baf26db0896531a07aa1e940d8a870b94e320d1 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:08 +0000 Subject: [PATCH 548/562] chore: bump genkit version to genkit@1.0.0-rc.16 --- js/genkit/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/genkit/package.json b/js/genkit/package.json index d9255076c..d5ffc91e4 100644 --- a/js/genkit/package.json +++ b/js/genkit/package.json @@ -7,7 +7,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "main": "./lib/cjs/index.js", "scripts": { From be33ea7a6bc185fcf3db66ad7377468dd409e083 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:10 +0000 Subject: [PATCH 549/562] chore: bump genkitx-chromadb version to genkitx-chromadb@1.0.0-rc.16 --- js/plugins/chroma/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/chroma/package.json b/js/plugins/chroma/package.json index d07d38079..1968dc3c7 100644 --- a/js/plugins/chroma/package.json +++ b/js/plugins/chroma/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From db7ea463e611be695cb48701ab9f05b965d2fcc4 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:13 +0000 Subject: [PATCH 550/562] chore: bump @genkit-ai/dev-local-vectorstore version to @genkit-ai/dev-local-vectorstore@1.0.0-rc.16 --- js/plugins/dev-local-vectorstore/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/dev-local-vectorstore/package.json b/js/plugins/dev-local-vectorstore/package.json index 376d479a7..b4f52ec2c 100644 --- a/js/plugins/dev-local-vectorstore/package.json +++ b/js/plugins/dev-local-vectorstore/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From fd372361c66a31d2459acba7a65b078889dca57a Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:16 +0000 Subject: [PATCH 551/562] chore: bump @genkit-ai/evaluator version to @genkit-ai/evaluator@1.0.0-rc.16 --- js/plugins/evaluators/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/evaluators/package.json b/js/plugins/evaluators/package.json index 9bcb3ea58..10eaad593 100644 --- a/js/plugins/evaluators/package.json +++ b/js/plugins/evaluators/package.json @@ -11,7 +11,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From ce8b58e9772c88f53a6b59faf04d37bfeec4d3ae Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:18 +0000 Subject: [PATCH 552/562] chore: bump @genkit-ai/firebase version to @genkit-ai/firebase@1.0.0-rc.16 --- js/plugins/firebase/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/firebase/package.json b/js/plugins/firebase/package.json index b545a1438..99b647706 100644 --- a/js/plugins/firebase/package.json +++ b/js/plugins/firebase/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From a8e7e18b10ebc1464994b60f74455ab0470c9e41 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:20 +0000 Subject: [PATCH 553/562] chore: bump @genkit-ai/google-cloud version to @genkit-ai/google-cloud@1.0.0-rc.16 --- js/plugins/google-cloud/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/google-cloud/package.json b/js/plugins/google-cloud/package.json index 1cd71ef90..b3115b783 100644 --- a/js/plugins/google-cloud/package.json +++ b/js/plugins/google-cloud/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From e35e762befa023c90a5d324f59699eae117ecab3 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:22 +0000 Subject: [PATCH 554/562] chore: bump @genkit-ai/googleai version to @genkit-ai/googleai@1.0.0-rc.16 --- js/plugins/googleai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/googleai/package.json b/js/plugins/googleai/package.json index 82050a34b..601210912 100644 --- a/js/plugins/googleai/package.json +++ b/js/plugins/googleai/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From 8900a3cb4f8b3a0ae00a52f5b9e99d119d644200 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:25 +0000 Subject: [PATCH 555/562] chore: bump genkitx-langchain version to genkitx-langchain@1.0.0-rc.16 --- js/plugins/langchain/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/langchain/package.json b/js/plugins/langchain/package.json index b261eb73a..aff4bedd6 100644 --- a/js/plugins/langchain/package.json +++ b/js/plugins/langchain/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From 3d780445864722673e5a3ab2bdc7d4711769e587 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:27 +0000 Subject: [PATCH 556/562] chore: bump genkitx-ollama version to genkitx-ollama@1.0.0-rc.16 --- js/plugins/ollama/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/ollama/package.json b/js/plugins/ollama/package.json index f875aaf5c..380ff9e84 100644 --- a/js/plugins/ollama/package.json +++ b/js/plugins/ollama/package.json @@ -10,7 +10,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From ecf6210593ff590823779f2249c1a59c3b754576 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:29 +0000 Subject: [PATCH 557/562] chore: bump genkitx-pinecone version to genkitx-pinecone@1.0.0-rc.16 --- js/plugins/pinecone/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/pinecone/package.json b/js/plugins/pinecone/package.json index ab2bca68b..ca90cfebf 100644 --- a/js/plugins/pinecone/package.json +++ b/js/plugins/pinecone/package.json @@ -13,7 +13,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From 1d9a86b0ed2fbbf8a446da55fd4e78eb5376b676 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:31 +0000 Subject: [PATCH 558/562] chore: bump @genkit-ai/vertexai version to @genkit-ai/vertexai@1.0.0-rc.16 --- js/plugins/vertexai/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/vertexai/package.json b/js/plugins/vertexai/package.json index 3c7578837..63c9368b1 100644 --- a/js/plugins/vertexai/package.json +++ b/js/plugins/vertexai/package.json @@ -17,7 +17,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From fb86a2c37ec18ae316e18d648e06e51ecd03d07d Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:34 +0000 Subject: [PATCH 559/562] chore: bump @genkit-ai/checks version to @genkit-ai/checks@1.0.0-rc.16 --- js/plugins/checks/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/checks/package.json b/js/plugins/checks/package.json index 8cb2d297a..6392aded4 100644 --- a/js/plugins/checks/package.json +++ b/js/plugins/checks/package.json @@ -13,7 +13,7 @@ "google checks", "guardrails" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From 8d89dd6287df752fe87919f251daf0fc0d3d3f23 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:36 +0000 Subject: [PATCH 560/562] chore: bump genkitx-mcp version to genkitx-mcp@1.0.0-rc.16 --- js/plugins/mcp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/mcp/package.json b/js/plugins/mcp/package.json index b39ae1aa9..46f883f41 100644 --- a/js/plugins/mcp/package.json +++ b/js/plugins/mcp/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "description": "A Genkit plugin that provides interoperability between Genkit and Model Context Protocol (MCP). Both client and server use cases are supported.", "main": "dist/index.js", "types": "./lib/index.d.ts", From f8a650c11edeafcbe0e5ab0e64053a51260d8317 Mon Sep 17 00:00:00 2001 From: Automated Version Bump Date: Tue, 4 Feb 2025 21:54:38 +0000 Subject: [PATCH 561/562] chore: bump @genkit-ai/express version to @genkit-ai/express@1.0.0-rc.16 --- js/plugins/express/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/plugins/express/package.json b/js/plugins/express/package.json index 0161b8851..b91ad4c93 100644 --- a/js/plugins/express/package.json +++ b/js/plugins/express/package.json @@ -9,7 +9,7 @@ "genai", "generative-ai" ], - "version": "1.0.0-rc.15", + "version": "1.0.0-rc.16", "type": "commonjs", "scripts": { "check": "tsc", From b27928ceef77bf0ed6f40943dbbe473fe80b8c4f Mon Sep 17 00:00:00 2001 From: Anthony Barone Date: Tue, 4 Feb 2025 17:12:09 -0500 Subject: [PATCH 562/562] chore: align all github workflows to use node v20 (#1840) --- .github/workflows/builder.yml | 2 +- .github/workflows/e2e-tests.yml | 2 +- .github/workflows/formatter.yml | 6 +++--- .github/workflows/tests.yml | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml index 5eb75618c..cfbc34c8e 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/builder.yml @@ -31,7 +31,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: 20.x - cache: 'pnpm' + cache: pnpm - name: Install dependencies run: pnpm install - name: Run build script diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 731744efc..0153a2ff1 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -30,7 +30,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: 20.x - cache: "pnpm" + cache: pnpm - name: Install dependencies run: pnpm install - name: Run build script diff --git a/.github/workflows/formatter.yml b/.github/workflows/formatter.yml index 23b012902..6921fa5fd 100644 --- a/.github/workflows/formatter.yml +++ b/.github/workflows/formatter.yml @@ -27,11 +27,11 @@ jobs: steps: - uses: actions/checkout@v3 - uses: pnpm/action-setup@v3 - - name: Set up node v21 + - name: Set up node v20 uses: actions/setup-node@v4 with: - node-version: 21.x - cache: 'pnpm' + node-version: 20.x + cache: pnpm - name: Install dependencies run: pnpm install - name: Check formatting diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 41241a936..896d284f7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,11 +27,11 @@ jobs: steps: - uses: actions/checkout@v3 - uses: pnpm/action-setup@v3 - - name: Set up node v21 + - name: Set up node v20 uses: actions/setup-node@v4 with: - node-version: 21.x - cache: 'pnpm' + node-version: 20.x + cache: pnpm - name: Install dependencies and build run: pnpm build:genkit-tools - name: Run tests

V>CQtIQEiQldJ1Upqs~zToTZT(yF!3*U)|iys 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%0AHYbVVvUfbs5l02<2UrG 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

V>CQtIQEiQldJ1Upqs~zToTZT(yF!3*U)|iys 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%0AHYbVVvUfbs5l02<2UrG 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